DEV Community

Cover image for 5 Steps how to track your Team's Mood
Adam
Adam

Posted on • Edited on • Originally published at urbanisierung.dev

1 1

5 Steps how to track your Team's Mood

Personally, I'm a very structured person. And as a rule, I also try to back up everything possible with data and facts in my private life. That helps me personally a lot to draw the right conclusions for actions.

Measuring your own mood is a metric to determine over time if something is wrong. Often people don't want to admit it to themselves, or judge the situation wrongly. Of course, the same measurement can be applied to entire teams. A team is only a strong team if the employees are satisfied and happy. Human mood pictures are an important asset, they should not only be treated anonymously but also primarily be in the data sovereignty of the teams (or at least the company). Of course, you can use external services for this, but you can also simply implement it yourself.

How about a self-built mood barometer for your team?

I have only simple requirements for it:

  • Simple and accessible channel to receive mood survey
  • Easy to configure trigger times
  • Mood value may only be submitted once
  • Anonymous results

I will again use a Camunda Cloud workflow engine for this use case. Why? Because I don't need to set up a cron job to poll the mood periodically, and because it allows me to easily extend the process.

A small backend is of course necessary

The backend should take care of sending and accepting the mood images. As channel I use a simple email. The email should contain five links that reflect the current mood. I chose email because everybody has an e-mail account. Be it in a private or business environment. No one has to register for a new service or install software for it.

E-Mail Template

I would like to use the following approach:

  1. A new process instance starts daily.
  2. A worker of a service task generates a random id for each participant and sends an email.
  3. Links in the email point to a web application that sends an HTTP request to a backend service with the mood.
  4. The backend service persists the data and checks beforehand if the request has already been submitted for the generated Id.

#1 Design the process

BPMN Diagram

Hardly worth mentioning, so simple and so valuable! A special feature is the start event. It is a timer event with the following configuration:

  • Timer Definition Type: Cycle
  • Timer Definition: R/P1D

With this configuration we tell the workflow engine to start a new instance every day.

#2 Implement the worker

The worker should do two things:

  1. Generate Id
  2. Send email

I've implemented the following controller to manage the moods:

import { v4 } from 'uuid'
import { Document } from '../types/Document.type'
import { Mood, MoodRequest } from '../types/Mood.type'
import { User } from '../types/User.type'
import { Error, ErrorType } from '../utils/Error'
import { StorageController } from './storage.controller'
export class MoodController {
constructor(private store: StorageController) {}
public createRequest(user: User) {
const now = new Date().getTime()
const moodRequest: MoodRequest = {
uuid: v4(),
team: user.team,
ts: now,
expiration: now + 1000 * 60 * 60 * 12,
}
return moodRequest
}
public async saveMoodRequest(moodRequest: MoodRequest) {
await this.store.set(Document.MOOD_REQUEST, moodRequest.uuid, moodRequest)
}
public async setMood(moodRequestId: string, moodValue: number) {
const moodRequest: MoodRequest = await this.store.get(
Document.MOOD_REQUEST,
moodRequestId,
)
if (!moodRequest) {
throw new Error(ErrorType.NotFound, `Mood not found`)
}
const now = new Date().getTime()
if (moodRequest.expiration < now) {
this.store.delete(Document.MOOD_REQUEST, moodRequestId)
throw new Error(ErrorType.BadRequest, `Request expired`)
}
const mood: Mood = {
uuid: moodRequest.uuid,
mood: moodValue,
team: moodRequest.team,
ts: now,
requestTs: moodRequest.ts,
}
await Promise.all([
this.store.delete(Document.MOOD_REQUEST, moodRequestId),
this.store.set(Document.MOOD, mood.uuid, mood),
])
}
}

Via createRequest() a new record is created with a random Id and the associated team. Then the record is stored in a database.

The worker uses this controller to create the request. Afterwards the email is sent via an SMTP server. For the sake of simplicity, the name, the email address and the associated team are set as parameters.

const nodemailer = require('nodemailer')
import * as functions from 'firebase-functions'
import { User } from '../../types/User.type'
import { MoodController } from '../mood.controller'
import { StorageController } from '../storage.controller'
import { ZeebeController } from '../zeebe.controller'
const PORT = 465
export class MailWorkerController {
constructor(
private zeebeController: ZeebeController,
private store: StorageController,
) {}
public createWorker(taskType: string) {
this.zeebeController.getZeebeClient().createWorker({
taskType,
taskHandler: async (job: any, complete: any, worker: any) => {
const user: User = {
name: job.customHeaders.name,
email: job.customHeaders.email,
team: job.customHeaders.team,
}
try {
await this.sendMail(user)
complete.success()
} catch (error) {
complete.failure('Failed to send email')
}
},
})
}
private async sendMail(user: User) {
const smtpUser = functions.config().email.user
const smtpPass = functions.config().email.pass
const smtpHost = functions.config().email.host
const baseUrl = functions.config().email.baseurl
const transporter = nodemailer.createTransport({
host: smtpHost,
port: PORT,
secure: true,
auth: {
user: smtpUser,
pass: smtpPass,
},
})
const subject = `How are you today?`
const moodController = new MoodController(this.store)
const moodRequest = moodController.createRequest(user)
await Promise.all([
moodController.saveMoodRequest(moodRequest),
transporter.sendMail({
from: `"Adam" <zeebe@urbanisierung.dev>`,
to: user.email,
subject,
text: `Please consider a modern email client.`,
html: this.createEmailHtml(user, baseUrl, moodRequest.uuid),
}),
])
}
private createEmailHtml(user: User, baseUrl: string, moodId: string) {
let html = `<p>Hello ${user.name},</p>
<p>How do you feel about your day today?</p>
<p>Please click in the term which better describes your day:</p>
<ul>
<li><a href="${baseUrl}/mood/${moodId}/5">Excellent day</a></li>
<li><a href="${baseUrl}/mood/${moodId}/4">Good day</a></li>
<li><a href="${baseUrl}/mood/${moodId}/3">Average day</a></li>
<li><a href="${baseUrl}/mood/${moodId}/2">Hard day</a></li>
<li><a href="${baseUrl}/mood/${moodId}/1">Bad day</a></li>
</ul>
<p>By giving your today's feeling, you'll be able to see the average mood of your teammates.</p>
<p>Cheers,</p>
<p>Adam</p>
`
return html
}
}
view raw mail.worker.ts hosted with ❤ by GitHub

The implementation can be further optimized, for example, by taking the user from an (internal) user database and passing only a userId. In addition, an email service such as SendGrid or MailJet can be used for sending emails.

#3 Setup a simple Backend

The backend is very simple, it accepts the request, checks if there is already a result for the id and if not it is persisted:

const express = require('express')
import { NextFunction, Request, Response } from 'express'
import { MoodController } from '../../controller/mood.controller'
import { StorageController } from '../../controller/storage.controller'
import { Error, ErrorType } from '../../utils/Error'
export class MoodRouter {
public router = express.Router({ mergeParams: true })
constructor(store: StorageController) {
this.router.post(
'/:moodRequestId/:moodValue',
async (req: Request, res: Response, next: NextFunction) => {
const moodRequestId: string = req.params.moodRequestId
const moodValue: number = Number(req.params.moodValue)
try {
if (!moodRequestId || !moodValue) {
throw new Error(ErrorType.BadRequest, `Mandatory parameter missing`)
}
if (moodValue < 1 || moodValue > 5) {
throw new Error(
ErrorType.BadRequest,
`Mood ${moodValue} is not in range [1,5]`,
)
}
const moodController = new MoodController(store)
await moodController.setMood(moodRequestId, moodValue)
res.send()
} catch (error) {
next(error)
}
},
)
}
}
view raw MoodRouter.ts hosted with ❤ by GitHub

#4 Use a Frontend to route your request to the Backend

Here I don't want to go into too much detail, it's just for routing the request ;)

#5 Start measuring!

With a few simple steps a service is created that can track your team's mood. Since the orchestrating component is a process, you can of course add more steps, or customize the flow to send an email only on certain days.

BPMN Diagram extended

And now: relax! :)

Alt Text


Let me know if the article was helpful! And if you like the content follow me on Twitter, LinkedIn or GitHub :)


Header Photo by Andrew Ridley on Unsplash

Last Photo by Taneli Lahtinen on Unsplash

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay