Managing Meal Breaks with Microsoft Teams

Preface

Actual solution cannot be shared. It does not include any HIPAA non-compliant information, but the solution does involve proprietary technologies owned by my employer. This was developed as a stop-gap solution to an issue posed as a change in legislation required more rigid break systems for Illinois based employers. This was not intended to be a permanent solution, rather an improvement over an existing one.

Background

In January 2023, the One Day Rest in Seven Act was passed in the state of Illinois. The largest item of note was the requirement for employers to only schedule employees a maximum of six continuous days. However, a smaller footnote would cement policies that most workplaces would follow, dictating when an employee is entitled to a half-hour meal break and a twenty (20) minute break. To ensure that all campuses complied with Illinois state law, our HR policy was amended to indicate the requirement of department heads and leadership to ensure that employees took their breaks. However, to effectively do this, there are 2 parts of each step in each part of the process. In my experience, the more complex an implementation is, the less likely a policy is to be followed fully. And as expected, this was what happened. Employees were not fully completing all steps to ensure proper documentation of their meal breaks. What then was this process?

  1. Employees would need to claim their meal break time at the start of their shift in a document to ensure department head tracking and access.
  2. Employees would need to indicate the start of their meal break by messaging a specific chat to ensure leadership knew the gap in coverage.
  3. Employees would need to indicate the return from their meal break by messaging a specific chat to ensure leadership knew the coverage was filled.
  4. Employees would need to sign off on their meal break time after their break was completed but before the end of their shift.

In comparison to how this was done by most other employers, it was clear that this was a short term solution waiting to burst at the seams for multiple reasons.

  1. Finding the correct document to claim their meal break time was difficult, especially for employees who were not regularly attached to a single computer. The vast majority of our department is designed to go between units to complete clerical work as needed.
  2. Finding the correct chat to indicate the start and end of a break suffered the same issue while existing in an entirely different location.
  3. Policies on properly indicating were communicated in a manner that did not clear up repeated confusion on the subject, most notably lacking examples of proper communication of break start and end times.

And as silly as it sounds, it only seemed odd to me that something wasn’t implemented sooner. Documenting meal times was normal for every other company I had worked for. However, since it was a new system, everyone in my department was complaining. It quickly became my point of frustration, not that it was complicated, but that people would not stop complaining that there wasn’t a better solution.

The Problem

It can be boiled down to a user experience issue. It’s cumbersome, complicated, and slow, and to the user, there is very little to get out of it. I think it would be most comparable to fishing in the intro of The Legend of Zelda: Twilight Princess, being strictly required to progress the story and grants no direct reward to the player. As my portfolio suggests, streamlining processes can be quite enjoyable to me and while we did not implement this pending a new official process being released the first of June, it still provided a learning experience.

Conceptualizing the solution

The actual problem is a solved one, which I think is important to note. We have a baseline that we can reference or augment to create a better solution. This also makes it easier to understand if progress was actually made. In general, there are only three (3) steps needed out of the employee.

  1. Employee claims their desired meal time.
  2. Employee communicates the gap in coverage.
  3. Employee communicates return to workstation.

But on the backend, we need this information communicated across two (2) different channels: a short term and a long term one. Leadership needs a quick point of reference to know if and when their subordinates have been breaked. Department heads need to know trends of how long and when employees go on break. While my department loves to use Microsoft Word inappropriately, the long-term answer is an Microsoft Excel workbook that includes separate sheets for every day with a table containing all pertinent information: employee, claimed slot, start time, and end time. And since we are already in Excel, calculating the duration of breaks is as trivial as =[@[End Time]]-[@[Start Time]]. Short term can be contained within a Microsoft Teams channel that will already inherit the corporate retention policy. There is still one missing piece of information which is the start times of each shift and their available claims. Illinois requires employees to have a meal break starting within the first 5 hours of their shift. So in addition to the data output table, we would need a data input table to say when the shifts start being when the claim cards should spawn and when the expected scheduled meal starts. I elected to also have this tied to the start time as breaks may start slightly earlier than designated. With a table containing all the information we need, we can pipe each row into a claim card. All we have to do is receive user input.

Adaptive Card Interface

Since we are using Teams, it would be easiest to use native systems to detect user input. Using Microsoft Power Automate, we can spawn adaptive cards one of a few ways. Adaptive cards are an easy way to create consistency in design across platforms for getting user input. This can range from a digital business card to a tee shirt order form. Since we are using Power Automate to pipe data through the claim cards, each row will be its own entity with the submit action adding additional information. In theory, we could do this with radio buttons, but it will make more sense why we feed it through this way later. The cards themselves end up being very basic, titled with the slot’s name, the anticipated start and end times, and a submit action button, but they will be shared top level inside the channel itself, not requiring the end user to navigate a file structure. Originally, I designed it all to happen concurrently using the Power Automate command “Post an adaptive card in a channel or chat and wait for user input”; however, this requires the explicit creation and declaration of every position within Power Automate’s flow. Since this is a stop-gap solution, this wouldn’t be too much of an issue, but it does mean that I would have to explicitly declare every position. Instead, I used the command “Post a card in a channel or chat”, this differs in that it does not wait for user input directly. It triggers an event “When user interacts with an adaptive card”, which can be paired one of the card’s properties. Now, we just set the trigger property for all cards to be a meal claim event.

Meal Start and Meal End

Similar to meal claim cards, we can use adaptive cards to test user inputs of meal start. However, to prevent meal start cards getting buried in the user’s direct messages, we only submit it just prior to the user’s meal start time. And just the same, we can use the same method to wait to just shy of the user’s meal’s end (possession in English is great). We can even throw on the times we expect the user to go from the meal properties that were piped from the source card and then the submission time of meal start card. One problem I have been dancing around though is a big one, one that requires tricking Power Automate to do something it isn’t exactly designed to do.

Halting and Waiting within Power Automate

Power Automate makes it incredibly easy to do things concurrently or sequentially, but what it doesn’t like to wait. At least in the sense of specific durations or till a specific time while staying within the same flow. Something you can find in some form in most scripting languages is simply missing as an explicit function or command. That is without thinking outside the box.
Power Automate features a module called Control that has a command called “Do Until”. By its name, you would think that it in fact can wait until a specific time, but it is more like most languages while statements. It does commands until a condition is met. The question becomes how do we make it check the time to make it our needed halt script command. The obvious solution is to tell it to check the time and make that our condition. Using the expression utcNow(), we can see the current time. Using a bit of nesting, we get convertFromUtc(utcNow(),’Central Standard Time’) so I don’t have to explain to my boss why keeping track of time zones is a headache. Unfortunately for me, JSON does not natively have a time object, and in the infinite wisdom of the universe, JSON stores date-time like objects as strings. So to effectively get the hour, we use the expression float(convertFromUtc(utcNow(),’Central Standard Time’,'HH.mm')). This should allow us to use functions for time being equal-to or greater-than-or-equal-to to directly compare now to our target time. Since we are only going as far as to check minutes, we can tell the function only to check every minute. Where we once again run into the problem, how do we make it pause for time?
We can nest “Do Until” commands within each other, and we can almost make “Do Until” commands run indefinitely. That is with a big asterisk of its exit conditions. “Do Until” will not let you run something run indefinitely, there must be a count or time restriction associated with the command. That time restriction cannot be piped in as a variable. That does allow us to cleverly make something that runs for exact durations of time, like 1 minute. So “Do Until 0 equals 1 for PT1M”. This however causes a new problem, “Do Until” requires commands to function. In the outer layer of “Do Until” have “Do Until” and update the variable representing the current time. The inner layer needs something happening, like a counter that ticks up for no reason.

Review

Overall, discovering the solution was informative about the limitations of Power Automate, but also how to work around these limitations. Unfortunately, unlike with the included flowcharts, Power Automate does not let the user call lambda functions or subroutines. I hope in the future that Power Automate adds features like “halt for this duration” or “halt until this time” in a more official capacity. The hacky nature of the actual process is endearing in a way, but it also can be seen as headache for readability. And since the new designer for Power Automate has less than official ways to add comments, it becomes a bit annoying to explain why things function the way they do.

Stray Thoughts

Power Automate is extremely powerful, even without being an owner on a Teams account. It still permits me to post adaptive cards and read data from them which could be a bit of a security concern if Teams was actually more secure than it is. The nature of Teams only has the level of security that exists within an instance of Active Directory and SharePoint, the former is well known by most organizations. The latter is a bit cryptic, as it acts like an older version of OneDrive, something that uses Active Directory user permissions as its structure. But since it’s its own beast, it functions in a wholly unique way. So while Teams feels like an obvious answer to quick communication, it comes with its own set of challenges that are better answered by programs like Slack or Discord better. Do keep in mind that each comes with their own EULAs which may not work for your company.

Spreadsheets to YAML

One of the most important ways to learn why mistakes are made is by documenting them. Often, recurring treads show why an issue or mistake arises. However, large volumes of data aren’t considered human readable, at least for most people. While in the process of documenting numbers associated with the units on the campus that I work, I found issues with many of our badges and the software that creates them that needed to be documented. Reporting such findings though could be done with software like Microsoft Excel, but the person in charge of overseeing the software does not have a history in data or computer science. As my experience with The Wind Waker Randomizer dv_im reminded me, YAML allows for the construction, maintenance, and readability of data sets, at the small cost of physical space.

YAML is a data language built on many JSON and Python sensibilities. White space denotes relations between objects and their properties. Objects can be built within dictionaries or lists. These two properties are perfect for reporting types of issues as well as keeping things easy to understand.

A basic VBA YAML constructor wouldn’t be too difficult, but a more flexible solution would prove better in the long run. I had originally written this article just after building the more flexible solution, which actually became quite useful. I was only documenting issues that were presenting themselves on visitor badges and patient profiles, but it became clear we also needed to record when software misbehaved or crashed. Having the solution already accommodate new object types was helpful.

Here’s how it was constructed. I knew I wanted two object types: patients and visitors. Each object type would have its own properties we needed to keep track of, as what issues we cared about were different between these two in our visitor management software. Having two different interpreters would be a lot of work for very little benefit. Instead, we know our data table exists in the UsedRange property of our Worksheet object. The top of each column, with the exception of the first, represents the properties of our YAML objects, such as Creator. Each row, with the exception of the first, represents an object. Each cell in a row, with the exception of the first, then is the value or quality of that property, such as zyarnot. The first column represents the dictionary we are building and the keys to the objects within the dictionary.

Knowing how the data is structured in Excel will greatly assist in building a good and efficient system for reading then writing it in the destination format. First, we build a template for the dictionary entries. These will always start with "{1}:" so that we can find where the key must go later. Each cell in this first row represents the title of each property as well as the data type of each property. Unlike VBA, YAML is not explicit with its data types, we are more concerned with data types like lists or non-data types like comments. Each property is added to our template as "{t#}: {v#}" with v being replaced with another character so later the interpreter can recognize what it needs to with the line. Despite the fact comments will be full width without title, I found titles must be declared and inserted before replacing the line, otherwise VBA would error out. Repeat this for each column until the template is complete.

Filling the templates with Replace() is quick work given that every type is implicit except lists and non-data comments, though lists are just an extra step of replacing semicolon-spaces with a newline, an extra tab and a dash with the data then following. Comments must find and replace the title as well as the empty data. Unlike with most YAML interpreters, I elected to have lines with undeclared properties delete the line. If we needed to convert from the YAML back to other methods, I would have the object declaration have optional variables that have default values of empty strings.

This will complete a whole dictionary without much effort. Each dictionary then is just reading a sheet in excel. Again, instead of explicitly declaring “read sheets in this list”, instead it goes through sheets between the how-to and output sheet. Every time the user clicks on the output sheet, data is read and written to it, dividing at about every 37 objects to prevent potential data loss due to cell rendering limits.

Here’s an example of what this simple approach can do:

Input

VisitorsBadgeCreatorIn-opCreatedIssueComments
McTest, John99995zyarnotzyarnot2023.02.05.17.08.25Misspelled first name (no “h”); Misspelled last name (extra “t” at end)How many S’s are in “misspelled”?
Doe, John99994zyarnotzyarnot2023.02.04.21.02.12Wrong category (Should be “Visitor”)

Output

Visitors:
 McTest, John:
  Badge: 99995
  Creator: zyarnot
  In-op: zyarnot
  Created: 2023.02.05.17.08.25
  Issue:
   - Misspelled first name (no "h")
   - Misspelled last name (extra "t" at end)

# How many S's are in "misspelled"?

 Doe, John:
  Badge: 99994
  Creator: zyarnot
  In-op: zyarnot
  Created: 2023.02.04.21.02.12
  Issue:
   - Wrong category (Should be "Visitor")

Zachary Yarnot and DualVission do not hold any rights to these owners’ contents.
Microsoft Excel and Visual Basic for Applications are products of Microsoft Corporation.
Microsoft, Microsoft Office, and Microsoft Excel are registered trademarks of Microsoft Corporation.