Skip to content

Commit

Permalink
Merge pull request #25 from Sergej-Popov/feature/average-point
Browse files Browse the repository at this point in the history
Add 'average point' option
  • Loading branch information
dgurkaynak authored Aug 15, 2020
2 parents 36c37fc + a76981c commit 81474ff
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 11 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ If you want to host your own app, follow this steps:
- Required user permission scopes: None
- User ID Translation
- **Turn off** "Translate Global IDs"
- Tokens
- Client ID, Secret and Verification token can be found on Basic Information page
- Installation
- Go to Manage Distribution, click "Add to Slack" and grant permissions

### Running via Docker

Expand Down
7 changes: 7 additions & 0 deletions src/routes/interactivity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,12 @@ export class InteractivityRoute {
selectedOptions,
(option) => option.value == 'protected'
);
const calculateAverage = !!find(
selectedOptions,
(option) => option.value == 'average'
);
span?.setAttributes({ isProtected: `${isProtected}` });
span?.setAttributes({ calculateAverage: `${calculateAverage}` });

// Create session struct
const session: ISession = {
Expand All @@ -461,6 +466,7 @@ export class InteractivityRoute {
participants,
rawPostMessageResponse: undefined,
protected: isProtected,
average: calculateAverage,
};
span?.setAttributes({
sessionId: session.id,
Expand Down Expand Up @@ -498,6 +504,7 @@ export class InteractivityRoute {
[ChannelSettingKey.PARTICIPANTS]: session.participants.join(' '),
[ChannelSettingKey.POINTS]: session.points.join(' '),
[ChannelSettingKey.PROTECTED]: JSON.stringify(session.protected),
[ChannelSettingKey.AVERAGE]: JSON.stringify(session.average),
})
);
if (upsertSettingErr) {
Expand Down
9 changes: 8 additions & 1 deletion src/routes/pp-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export class PPCommandRoute {
[ChannelSettingKey.PARTICIPANTS]: [] as string[],
[ChannelSettingKey.POINTS]: DEFAULT_POINTS,
[ChannelSettingKey.PROTECTED]: false,
[ChannelSettingKey.AVERAGE]: false,
};
if (channelSettings?.[ChannelSettingKey.PARTICIPANTS]) {
settings[ChannelSettingKey.PARTICIPANTS] = channelSettings[
Expand All @@ -219,6 +220,11 @@ export class PPCommandRoute {
channelSettings[ChannelSettingKey.PROTECTED]
);
}
if (channelSettings?.[ChannelSettingKey.AVERAGE]) {
settings[ChannelSettingKey.AVERAGE] = JSON.parse(
channelSettings[ChannelSettingKey.AVERAGE]
);
}

await SessionController.openModal({
triggerId: cmd.trigger_id,
Expand All @@ -228,6 +234,7 @@ export class PPCommandRoute {
participants: settings[ChannelSettingKey.PARTICIPANTS],
points: settings[ChannelSettingKey.POINTS],
isProtected: settings[ChannelSettingKey.PROTECTED],
calculateAverage: settings[ChannelSettingKey.AVERAGE],
});

// Send acknowledgement back to API -- HTTP 200
Expand Down Expand Up @@ -269,7 +276,7 @@ export class PPCommandRoute {
static async configure(cmd: ISlackCommandRequestBody, res: express.Response) {
return res.json({
text:
'This command is depracated. The session settings (points, participants, ...) ' +
'This command is deprecated. The session settings (points, participants, ...) ' +
'are now persisted automatically for each channel/conversation.',
response_type: 'ephemeral',
replace_original: false,
Expand Down
54 changes: 46 additions & 8 deletions src/session/session-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export class SessionController {
participants,
points,
isProtected,
calculateAverage,
}: {
triggerId: string;
team: ITeam;
Expand All @@ -73,6 +74,7 @@ export class SessionController {
participants: string[];
points: string[];
isProtected: boolean;
calculateAverage: boolean;
}) {
const slackWebClient = new WebClient(team.access_token);

Expand All @@ -85,6 +87,25 @@ export class SessionController {
value: 'protected',
} as any;

const averageCheckboxesOption = {
text: {
type: 'plain_text',
text: 'Calculate the average (only numeric points will be used)',
emoji: true,
},
value: 'average',
} as any;

let initialOptions = undefined;
if (isProtected) {
initialOptions = initialOptions || [];
initialOptions.push(protectedCheckboxesOption);
}
if (calculateAverage) {
initialOptions = initialOptions || [];
initialOptions.push(averageCheckboxesOption);
}

await slackWebClient.views.open({
trigger_id: triggerId,
view: {
Expand Down Expand Up @@ -158,7 +179,7 @@ export class SessionController {
},
hint: {
type: 'plain_text',
text: 'Enter points seperated by space',
text: 'Enter points separated by space',
emoji: true,
},
label: {
Expand All @@ -173,10 +194,8 @@ export class SessionController {
optional: true,
element: {
type: 'checkboxes',
options: [protectedCheckboxesOption],
initial_options: isProtected
? [protectedCheckboxesOption]
: undefined,
options: [protectedCheckboxesOption, averageCheckboxesOption],
initial_options: initialOptions
},
label: {
type: 'plain_text',
Expand Down Expand Up @@ -312,12 +331,18 @@ export class SessionController {
})
.join('\n');

let averageText = "";
if (session.average) {
const average = SessionController.getAverage(session.votes);
averageText = average ? `\nAverage: ${SessionController.getAverage(session.votes)}` : "";
}

await slackWebClient.chat.update({
ts: session.rawPostMessageResponse.ts,
channel: session.rawPostMessageResponse.channel,
text: userId
? `Title: *${session.title}* (revealed by <@${userId}>)\n\nResult:\n${votesText}`
: `Title: *${session.title}*\n\nResult:\n${votesText}`,
? `Title: *${session.title}* (revealed by <@${userId}>)\n\nResult:\n${votesText}${averageText}`
: `Title: *${session.title}*\n\nResult:\n${votesText}${averageText}`,
attachments: [],
});
} else if (session.state == 'cancelled') {
Expand Down Expand Up @@ -347,10 +372,23 @@ export class SessionController {
}
}

/**
* For given votes, calculate average point
*/
static getAverage(votes: { [key: string]: string }): string | boolean {
const numericPoints = Object.values(votes).filter(SessionController.isNumeric).map(parseFloat);
if (numericPoints.length < 1) return false;
return (numericPoints.reduce((a, b) => a + b) / numericPoints.length).toFixed(1);
}

static isNumeric(n: any) {
return !isNaN(parseFloat(n)) && isFinite(n);
}

/**
* For a given slack slash-command text, extract mentions
*/
static exractMentions(text: string) {
static extractMentions(text: string) {
const allMentions: ISessionMention[] = [];

// User mentions
Expand Down
4 changes: 4 additions & 0 deletions src/session/session-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export interface ISession {
* can cancel and reveal session.
*/
protected: boolean;
/**
* Whether to calculate the average from numeric points.
*/
average: boolean;
}

// If `process.env.USE_REDIS` is falsy, in-memory db will be used
Expand Down
3 changes: 2 additions & 1 deletion src/team/team-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export interface ITeam {
export enum ChannelSettingKey {
PARTICIPANTS = 'participants',
POINTS = 'points',
PROTECTED = 'proctected',
PROTECTED = 'protected',
AVERAGE = "average",
}

export interface IChannelSetting {
Expand Down
3 changes: 2 additions & 1 deletion src/views/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ <h4 id="features">Features</h4>
use on that channel, including participants and poker points.
</li>
<li>Protected sessions: prevent others to reveal/cancel the sessions you've created.</li>
<li>Average point: calculate average point to aid decision making process .</li>
<li>The sessions are kept in database for 48 hours, after that it's completely gone.</li>
</ul>
</p>

<h4 id="usage">Usage</h4>
<p>
After successful installation, <code>/pp</code>
slash command will be avaliable in your team workspace. It works in: <br />
slash command will be available in your team workspace. It works in: <br />

<ul>
<li>Public channels</li>
Expand Down

0 comments on commit 81474ff

Please sign in to comment.