Skip to content

Commit 75d4d1f

Browse files
committed
Added members for a team and exceptions for assigned personnel
1 parent 6bf9de9 commit 75d4d1f

File tree

13 files changed

+503
-13
lines changed

13 files changed

+503
-13
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,23 @@ The diagram below illustrates Business Management Systems' DNO (Day, Night, Off)
1111

1212
A shift is defined with a name, description, starting time of day and duration. An off-shift period is associated with a shift. In the example above for Team1, there are two shifts followed by one off-shift period. Shifts can be overlapped (typically when a handoff of duties is important such as in the nursing profession). A rotation is a sequence of shifts and off-shift days. The DNO rotation is Day on, Night on and Night off. An instance of a shift has a starting date and time of day and has an associated shift definition.
1313

14+
*Break*
15+
16+
A break is a defined working period of time during a shift, for example lunch. A shift can have zero or more breaks.
17+
1418
*Team*
1519

1620
A team is defined with a name and description. It has a rotation with a starting date. The starting date shift will have an instance with that date and a starting time of day as defined by the shift. The same rotation can be shared between more than one team, but with different starting times.
1721

22+
*Team Member*
23+
24+
A team member is a person assigned to a team. The member is identified by a member ID (e.g. employee ID), name and description/title.
25+
26+
*Team Member Exception*
27+
28+
A team member exception is an addition and/or removal from the assigned members of a team for a specified shift instance. The instance is identified by the starting date and time.
29+
30+
1831
*Work Schedule*
1932

2033
A work schedule is defined with a name and description. It has one or more teams. Zero or more non-working periods can be defined. A non-working period has a defined starting date and time of day and duration. For example, the New Year's Day holiday starting at midnight for 24 hours, or three consecutive days for preventive maintenance of manufacturing equipment starting at the end of the night shift.
@@ -219,6 +232,39 @@ Working shifts
219232
(1) Team: Green, Shift: 24 Hour, Start : 2017-02-07T07:00, End : 2017-02-08T07:00
220233
```
221234

235+
For a fourth example, for a restaurant shift starting at 7 am on August 8, 2024, team member #1 called in sick and is to be replaced by member #10:
236+
```cs
237+
WorkSchedule schedule = new WorkSchedule("Restaurant", "Two shifts");
238+
239+
// day shift (12 hours)
240+
Shift day = schedule.CreateShift("Day", "Green", new LocalTime(6, 0, 0), Duration.FromHours(12));
241+
242+
// day shift rotation, 1 days ON, 0 OFF
243+
Rotation dayRotation = schedule.CreateRotation("Day", "One day on, 6 off");
244+
dayRotation.AddSegment(day, 1, 6);
245+
246+
LocalDate greenStart = new LocalDate(2024, 7, 28);
247+
Team sundayDay = schedule.CreateTeam("SundayDay", "Sunday day", dayRotation, greenStart);
248+
249+
// chef members
250+
TeamMember one = new TeamMember("Chef, One", "Chef", "1");
251+
TeamMember ten = new TeamMember("Ten", "Ten description", "10");
252+
253+
sundayDay.AddMember(one);
254+
255+
// replace member one with ten
256+
LocalDateTime exceptionShift = new LocalDateTime(2024, 8, 11, 7, 0, 0);
257+
TeamMemberException replacement = new TeamMemberException(exceptionShift);
258+
replacement.Removal = one;
259+
replacement.Addition = ten;
260+
sundayDay.AddMemberException(replacement);
261+
262+
// #1 is in the assigned list
263+
List<TeamMember> members = sundayDay.AssignedMembers;
264+
265+
// but is replaced by #10 for that shift instance:
266+
members = sundayDay.GetMembers(exceptionShift);
267+
```
222268
## Project Structure
223269
ShiftSharp depends upon .Net Framework 4.5+ due to use of the NodaTime date and time classes.
224270

Release Notes.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@
44

55
v2.0.0: January 20, 2024
66
- Upgraded v1.0.0 to .NET 8
7+
- Published to NuGet
8+
9+
v2.1.0: December 17, 2024
10+
- Added members for a team and exceptions for assigned personnel
711
- Published to NuGet
140 KB
Binary file not shown.

ShiftSharp/Properties/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,4 +222,10 @@
222222
<data name="team.hours" xml:space="preserve">
223223
<value>Average hours worked per week</value>
224224
</data>
225+
<data name="team.members" xml:space="preserve">
226+
<value>Members:</value>
227+
</data>
228+
<data name="member.id" xml:space="preserve">
229+
<value>ID</value>
230+
</data>
225231
</root>

ShiftSharp/ShiftInstance.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,15 @@ public override string ToString()
101101
string s = WorkSchedule.GetMessage("shift");
102102
string ps = WorkSchedule.GetMessage("period.start");
103103
string pe = WorkSchedule.GetMessage("period.end");
104+
String members = WorkSchedule.GetMessage("team.members");
104105

105106
string text = " " + t + ": " + Team.Name + ", " + s + ": " + Shift.Name + ", " + ps + ": "
106-
+ StartDateTime + ", " + pe + ": " + GetEndTime();
107+
+ StartDateTime + ", " + pe + ": " + GetEndTime() +"\n" + members;
108+
109+
foreach (TeamMember member in this.Team.GetMembers(StartDateTime))
110+
{
111+
text += "\n\t" + member;
112+
}
107113
return text;
108114
}
109115
}

ShiftSharp/ShiftSharp.csproj

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,21 @@
88
<Description>C# work schedule library</Description>
99
<Company>Point85</Company>
1010
<Product>ShiftSharp</Product>
11-
<Copyright>Copyright © 2017</Copyright>
12-
<AssemblyVersion>2.0.0.0</AssemblyVersion>
13-
<FileVersion>2.0.0.0</FileVersion>
11+
<Copyright>Copyright © 2017-2024</Copyright>
12+
<AssemblyVersion>2.1.0.0</AssemblyVersion>
13+
<FileVersion>2.1.0.0</FileVersion>
1414
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
1515
<Title>Work schedule library</Title>
16-
<Version>2.0.1</Version>
16+
<Version>2.1.0</Version>
17+
<Authors>Kent Randall</Authors>
18+
<PackageId>ShiftSharp</PackageId>
1719
<PackageProjectUrl>https://github.com/point85/ShiftSharp</PackageProjectUrl>
1820
<PackageIcon>WorkSchedule.png</PackageIcon>
1921
<PackageReadmeFile>README.md</PackageReadmeFile>
2022
<RepositoryUrl>https://github.com/point85/ShiftSharp</RepositoryUrl>
2123
<RepositoryType>git</RepositoryType>
2224
<PackageTags>C#; shift; work schedule; shift calendar; work calendar</PackageTags>
23-
<PackageReleaseNotes>Upgraded to .NET 8</PackageReleaseNotes>
25+
<PackageReleaseNotes>Added team members and exceptions</PackageReleaseNotes>
2426
<PackageLicenseFile>LICENSE</PackageLicenseFile>
2527
<ApplicationIcon>WorkSchedule.ico</ApplicationIcon>
2628
<Nullable>disable</Nullable>

ShiftSharp/Team.cs

Lines changed: 133 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2424

2525
using NodaTime;
2626
using System;
27+
using System.Collections.Concurrent;
28+
using System.Collections.Generic;
29+
using static System.Runtime.InteropServices.JavaScript.JSType;
2730

2831
namespace Point85.ShiftSharp.Schedule
2932
{
@@ -47,6 +50,19 @@ public class Team : Named, IComparable<Team>
4750
/// </summary>
4851
public Rotation Rotation { get; set; }
4952

53+
/// <summary>
54+
/// Team members assigned to work regular shifts
55+
/// </summary>
56+
public List<TeamMember> AssignedMembers { get; private set; } = new List<TeamMember>();
57+
58+
/// <summary>
59+
/// Team member exceptions to regular shifts
60+
/// </summary>
61+
public List<TeamMemberException> MemberExceptions { get; private set; } = new List<TeamMemberException>();
62+
63+
// team member exception cache by shift instance start
64+
private ConcurrentDictionary<LocalDateTime, TeamMemberException> ExceptionCache;
65+
5066
/// <summary>
5167
/// Constructor
5268
/// </summary>
@@ -273,21 +289,136 @@ public int CompareTo(Team other)
273289
public override string ToString()
274290
{
275291
string rpct = WorkSchedule.GetMessage("rotation.percentage");
276-
277292
string rs = WorkSchedule.GetMessage("rotation.start");
278293
string avg = WorkSchedule.GetMessage("team.hours");
294+
string members = WorkSchedule.GetMessage("team.members");
279295

280296
string text = "";
281297
try
282298
{
283299
text = base.ToString() + ", " + rs + ": " + RotationStart + ", " + Rotation + ", " + rpct + ": "
284-
+ GetPercentageWorked().ToString("0.00") + "%" + ", " + avg + ": " + GetHoursWorkedPerWeek();
300+
+ GetPercentageWorked().ToString("0.00") + "%" + ", " + avg + ": " + GetHoursWorkedPerWeek() + "\n"
301+
+ members;
302+
303+
foreach (TeamMember member in AssignedMembers)
304+
{
305+
text += "\n\t" + member;
306+
}
285307
}
286308
catch (Exception)
287309
{
288310
}
289311

290312
return text;
291313
}
314+
315+
/// <summary>
316+
/// Add a member to this team
317+
/// </summary>
318+
/// <param name="member">Team member</param>
319+
public void AddMember(TeamMember member)
320+
{
321+
if (!this.AssignedMembers.Contains(member))
322+
{
323+
this.AssignedMembers.Add(member);
324+
}
325+
}
326+
327+
/// <summary>
328+
/// Remove a member from this team
329+
/// </summary>
330+
/// <param name="member">Team member</param>
331+
public void removeMember(TeamMember member)
332+
{
333+
if (this.AssignedMembers.Contains(member))
334+
{
335+
this.AssignedMembers.Remove(member);
336+
}
337+
}
338+
339+
/// <summary>
340+
/// True if member is assigned to this team
341+
/// </summary>
342+
/// <param name="member">Team member</param>
343+
/// <returns>bool</returns>
344+
public bool HasMember(TeamMember member)
345+
{
346+
return this.AssignedMembers.Contains(member);
347+
}
348+
349+
/// <summary>
350+
/// Add a member exception for this team
351+
/// </summary>
352+
/// <param name="memberException">Team member exception</param>
353+
public void AddMemberException(TeamMemberException memberException)
354+
{
355+
this.MemberExceptions.Add(memberException);
356+
357+
// invalidate cache
358+
this.ExceptionCache = null;
359+
}
360+
361+
/// <summary>
362+
/// Remove a member exception for this team
363+
/// </summary>
364+
/// <param name="memberException">Team member exception</param>
365+
public void RemoveMemberException(TeamMemberException memberException)
366+
{
367+
this.MemberExceptions.Remove(memberException);
368+
369+
// invalidate cache
370+
this.ExceptionCache = null;
371+
}
372+
373+
/// <summary>
374+
/// Build a list of team member for the specified shift start
375+
/// </summary>
376+
/// <param name="shiftStart">Shift instance starting date and time</param>
377+
/// <returns>List of team members</returns>
378+
379+
public List<TeamMember> GetMembers(LocalDateTime shiftStart)
380+
{
381+
List<TeamMember> members = new List<TeamMember>();
382+
383+
// build the cache if not already done
384+
BuildMemberCache();
385+
386+
// members assigned to the team
387+
foreach (TeamMember member in AssignedMembers)
388+
{
389+
members.Add(member);
390+
}
391+
392+
// any exceptions?
393+
if (ExceptionCache.TryGetValue(shiftStart, out TeamMemberException tme))
394+
{
395+
if (tme.Addition != null)
396+
{
397+
members.Add(tme.Addition);
398+
}
399+
400+
if (tme.Removal!= null)
401+
{
402+
members.Remove(tme.Removal);
403+
}
404+
}
405+
return members;
406+
}
407+
408+
private void BuildMemberCache()
409+
{
410+
if (ExceptionCache == null)
411+
{
412+
// create it
413+
ExceptionCache = new ConcurrentDictionary<LocalDateTime, TeamMemberException>();
414+
}
415+
416+
ExceptionCache.Clear();
417+
418+
foreach (TeamMemberException tme in MemberExceptions)
419+
{
420+
ExceptionCache[tme.DateTime] = tme;
421+
}
422+
}
292423
}
293424
}

0 commit comments

Comments
 (0)