Skip to content

Commit

Permalink
Make intro more robust
Browse files Browse the repository at this point in the history
GitOrigin-RevId: deb95169db5f62275d7569eba67d433d04bc56fd
  • Loading branch information
alyssachvasta authored and copybara-github committed Dec 17, 2024
1 parent 45f8770 commit 1a9f9f0
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 175 deletions.
57 changes: 56 additions & 1 deletion src/sensemaker_utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import { getPrompt } from "./sensemaker_utils";
import { getPrompt, groupCommentsBySubtopic } from "./sensemaker_utils";
import { Comment } from "./types";

describe("SensemakerUtilsTest", () => {
it("should create a prompt", () => {
Expand Down Expand Up @@ -45,4 +46,58 @@ comment1
comment2`
);
});
describe("groupCommentsByTopic", () => {
it("should group comments by topic and subtopic", () => {
const categorizedComments: Comment[] = [
{
id: "1",
text: "Comment 1",
topics: [
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.1" }] },
{ name: "Topic 2", subtopics: [{ name: "Subtopic 2.1" }] },
],
},
{
id: "2",
text: "Comment 2",
topics: [
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.1" }] },
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.2" }] },
],
},
];

const expectedOutput = {
"Topic 1": {
"Subtopic 1.1": {
"1": "Comment 1",
"2": "Comment 2",
},
"Subtopic 1.2": {
"2": "Comment 2",
},
},
"Topic 2": {
"Subtopic 2.1": {
"1": "Comment 1",
},
},
};

const result = groupCommentsBySubtopic(categorizedComments);
expect(result).toEqual(expectedOutput);
});

it("should skip comment if it has no topics", () => {
const categorizedComments: Comment[] = [
{
id: "1",
text: "Comment 1",
topics: [], // No topics assigned
},
];

expect(groupCommentsBySubtopic(categorizedComments)).toEqual({});
});
});
});
9 changes: 7 additions & 2 deletions src/sensemaker_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,20 @@ export function hydrateCommentRecord(
*
* TODO: create a similar function to group comments by topics only.
*/
export function groupCommentsBySubtopic(categorized: Comment[]) {
export function groupCommentsBySubtopic(categorized: Comment[]): {
[topicName: string]: {
[subtopicName: string]: { [commentId: string]: string };
};
} {
const groupedComments: {
[topicName: string]: {
[subtopicName: string]: { [commentId: string]: string };
};
} = {};
for (const comment of categorized) {
if (!comment.topics || comment.topics.length === 0) {
throw new Error(`Comment with ID ${comment.id} has no topics assigned.`);
console.log(`Comment with ID ${comment.id} has no topics assigned.`);
continue;
}
for (const topic of comment.topics) {
if (!groupedComments[topic.name]) {
Expand Down
99 changes: 99 additions & 0 deletions src/stats_util.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import { SummaryStats } from "./stats_util";
import { Comment } from "./types";

const TEST_COMMENTS = [
{
id: "1",
text: "comment1",
voteTalliesByGroup: {
"0": {
agreeCount: 10,
disagreeCount: 5,
passCount: 0,
totalCount: 15,
},
"1": {
agreeCount: 5,
disagreeCount: 10,
passCount: 5,
totalCount: 20,
},
},
},
{
id: "2",
text: "comment2",
voteTalliesByGroup: {
"0": {
agreeCount: 2,
disagreeCount: 5,
passCount: 3,
totalCount: 10,
},
"1": {
agreeCount: 5,
disagreeCount: 3,
passCount: 2,
totalCount: 10,
},
},
},
];

describe("StatsUtilTest", () => {
it("should get the total number of votes from multiple comments", () => {
const summaryStats = new SummaryStats(TEST_COMMENTS);
expect(summaryStats.voteCount).toEqual(55);
});

it("SummaryStats should get the total number of comments", () => {
const summaryStats = new SummaryStats(TEST_COMMENTS);
expect(summaryStats.commentCount).toEqual(2);
});

it("should count comments by topic", () => {
const comments: Comment[] = [
{
id: "1",
text: "comment 1",
topics: [{ name: "Topic A", subtopics: [{ name: "Subtopic A.1" }] }],
},
{
id: "2",
text: "comment 2",
topics: [{ name: "Topic A", subtopics: [{ name: "Subtopic A.1" }] }],
},
{
id: "3",
text: "comment 3",
topics: [{ name: "Topic A", subtopics: [{ name: "Subtopic A.2" }] }],
},
];

const expectedTopicStats = [
{
name: "Topic A",
commentCount: 3,
subtopicStats: [
{ name: "Subtopic A.1", commentCount: 2 },
{ name: "Subtopic A.2", commentCount: 1 },
],
},
];
expect(new SummaryStats(comments).getStatsByTopic()).toEqual(expectedTopicStats);
});
});
91 changes: 91 additions & 0 deletions src/stats_util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Utils to get statistical information from a deliberation

import { Comment } from "./types";
import { groupCommentsBySubtopic } from "./sensemaker_utils";

// Statistics to include in the summary.
export class SummaryStats {
comments: Comment[];
constructor(comments: Comment[]) {
this.comments = comments;
}

private getCommentVoteCount(comment: Comment): number {
let count = 0;
for (const groupName in comment.voteTalliesByGroup) {
const groupCount = comment.voteTalliesByGroup[groupName].totalCount;
if (groupCount > 0) {
count += groupCount;
}
}
return count;
}

// The total number of votes in all comments in a deliberation.
get voteCount(): number {
return this.comments.reduce((sum: number, comment: Comment) => {
return sum + this.getCommentVoteCount(comment);
}, 0);
}

// The total number of comments in a deliberation.
get commentCount(): number {
return this.comments.length;
}

/**
* Counts the number of comments associated with each topic and subtopic.
*
* @param commentsByTopic A nested map where keys are topic names, values are maps
* where keys are subtopic names, and values are maps where
* keys are comment IDs and values are comment texts.
* @returns An array of `TopicStats` objects.
*/
getStatsByTopic(): TopicStats[] {
const commentsByTopic = groupCommentsBySubtopic(this.comments);
const topicStats: TopicStats[] = [];

for (const topicName in commentsByTopic) {
const subtopics = commentsByTopic[topicName];
const subtopicStats: TopicStats[] = [];
let totalTopicComments = 0;

for (const subtopicName in subtopics) {
const commentCount = Object.keys(subtopics[subtopicName]).length;
totalTopicComments += commentCount;
subtopicStats.push({ name: subtopicName, commentCount });
}

topicStats.push({
name: topicName,
commentCount: totalTopicComments,
subtopicStats: subtopicStats,
});
}

return topicStats;
}
}

/**
* Represents statistics about a topic and its subtopics.
*/
export interface TopicStats {
name: string;
commentCount: number;
subtopicStats?: TopicStats[];
}
58 changes: 0 additions & 58 deletions src/tasks/categorization.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import {
} from "./categorization";
import { CommentRecord, Comment, Topic } from "../types";
import { VertexModel } from "../models/vertex_model";
import { groupCommentsBySubtopic } from "../sensemaker_utils";

// Mock the model response. This mock needs to be set up to return response specific for each test.
let mockGenerateData: jest.SpyInstance;
Expand Down Expand Up @@ -495,61 +494,4 @@ describe("findMissingComments", () => {
{ id: "2", text: "Comment 2" },
]);
});

describe("groupCommentsByTopic", () => {
it("should group comments by topic and subtopic", () => {
const categorizedComments: Comment[] = [
{
id: "1",
text: "Comment 1",
topics: [
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.1" }] },
{ name: "Topic 2", subtopics: [{ name: "Subtopic 2.1" }] },
],
},
{
id: "2",
text: "Comment 2",
topics: [
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.1" }] },
{ name: "Topic 1", subtopics: [{ name: "Subtopic 1.2" }] },
],
},
];

const expectedOutput = {
"Topic 1": {
"Subtopic 1.1": {
"1": "Comment 1",
"2": "Comment 2",
},
"Subtopic 1.2": {
"2": "Comment 2",
},
},
"Topic 2": {
"Subtopic 2.1": {
"1": "Comment 1",
},
},
};

const result = groupCommentsBySubtopic(categorizedComments);
expect(result).toEqual(expectedOutput);
});

it("should throw an error if a comment has no topics", () => {
const categorizedComments: Comment[] = [
{
id: "1",
text: "Comment 1",
topics: [], // No topics assigned
},
];

expect(() => groupCommentsBySubtopic(categorizedComments)).toThrow(
"Comment with ID 1 has no topics assigned."
);
});
});
});
Loading

0 comments on commit 1a9f9f0

Please sign in to comment.