Skip to content

Commit 326111a

Browse files
authored
Tester Response and Moderation Phase changes (#151)
* add issue comment to issue model * change testerresponse to read from comment * add guard condition * fix bug where team response is read again * submit testerresponse also updates issue comment * add spinner while comment is loading * filter to check for team response * flatMap for createIssueModel * revert changes to table filter * change item title to contain question mark * Asynchronous Comment Loading Established * Fix IssueComment Undefined Error * fix bug where comment is not loaded properly * add filter to table * Parsing Label Changes from Team Response * Label Parsing from Issue Comment * Fix parsing of tester response New Regex correctly Parses tester responses * [WIP] Issue 144 Moderation phase (#5) * add dispute component to moderation phase * function to create new comment for tutor response * fix bug student settutorresponse * set pending for disputes * todolist for disputes * code cleaning * code cleaning * Further TesterResp Parsing Regex Fixes * LabelExtraction Regex Flexibility added * Further labelExtraction Constraints & Flexibility * parseTesterResp. regex flexibility added * Further Regex Flexibility Changes Changes done to parseTutorResponseInComment and parseIssueDisputes * code cleaning and comment * revert concat string * Case Insensitivity Added to Regex * Regex Pattern Spaces Set to Any Number * add unsure checkbox * fix bug where description format for bugreporting and testerresponse are different * update comment for model * clean up issue comment * revert previous commit * clean up issue comment code and add support for filtering multiple comment * documentation for issue comment * change version number in package.json
1 parent a97674a commit 326111a

26 files changed

+643
-103
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "CATcher",
3-
"version": "1.0.0",
3+
"version": "3.1.0",
44
"main": "main.js",
55
"scripts": {
66
"postinstall": "npm run postinstall:electron && electron-builder install-app-deps",
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export class IssueDispute {
2+
readonly TODO_UNCHECKED = '- [ ] Done';
3+
readonly INITIAL_RESPONSE = '[replace this with your explanation]';
4+
readonly TITLE_PREFIX = '## :question: ';
5+
readonly LINE_BREAK = '-------------------\n';
6+
title: string; // e.g Issue severity
7+
description: string; // e.g Team says: xxx\n Tester says: xxx.
8+
tutorResponse: string; // e.g Not justified. I've changed it back.
9+
todo: string; // e.g - [x] Done
10+
11+
constructor(title: string, description: string) {
12+
this.title = title;
13+
this.description = description;
14+
this.tutorResponse = this.INITIAL_RESPONSE;
15+
this.todo = this.TODO_UNCHECKED;
16+
}
17+
18+
/*
19+
This method is used to format the tutor's response so that the app can upload it on Github.
20+
Refer to format in https://github.com/CATcher-org/templates#app-collect-tutor-response
21+
*/
22+
toTutorResponseString(): string {
23+
let toString = '';
24+
toString += this.TITLE_PREFIX + this.title + '\n\n';
25+
toString += this.todo + '\n\n';
26+
toString += this.tutorResponse + '\n\n';
27+
toString += this.LINE_BREAK;
28+
return toString;
29+
}
30+
31+
toString(): string {
32+
let toString = '';
33+
toString += this.TITLE_PREFIX + this.title + '\n\n';
34+
toString += this.description + '\n\n';
35+
toString += this.LINE_BREAK;
36+
return toString;
37+
}
38+
}

src/app/core/models/issue.model.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import {Team} from './team.model';
22
import { TesterResponse } from './tester-response.model';
3+
import { IssueComment, IssueComments } from './comment.model';
4+
import { IssueDispute } from './issue-dispute.model';
35

46
export interface Issue {
57
readonly id: number;
@@ -18,6 +20,10 @@ export interface Issue {
1820
teamResponse?: string;
1921
tutorResponse?: string;
2022
testerResponses?: TesterResponse[];
23+
issueComment?: IssueComment; // Issue comment is used for Tutor Response and Tester Response
24+
issueDisputes?: IssueDispute[];
25+
pending?: string;
26+
unsure?: boolean;
2127
}
2228

2329
export interface Issues {
@@ -29,13 +35,15 @@ export interface Issues {
2935
* Where `Type` represent the type of the label. (e.g. severity, type, response)
3036
* And `Value` represent the value that is associated to the `Type` (e.g. for severity Type, it could be Low, Medium, High)
3137
*/
32-
export const LABELS = ['severity', 'type', 'response', 'duplicate', 'status'];
38+
export const LABELS = ['severity', 'type', 'response', 'duplicate', 'status', 'pending', 'unsure'];
3339

3440
export const labelsToAttributeMapping = {
3541
'severity': 'severity',
3642
'type': 'type',
3743
'response': 'responseTag',
3844
'status': 'status',
45+
'pending': 'pending',
46+
'unsure': 'unsure'
3947
};
4048

4149
export const SEVERITY_ORDER = { Low: 0, Medium: 1, High: 2 };
@@ -107,6 +115,6 @@ export const phaseTesterResponseDescriptionTemplate = new RegExp('(?<header># De
107115
'|## State the duplicated issue here, if any|# Items for the Tester to Verify|$)', 'gi');
108116

109117
export const phaseModerationDescriptionTemplate = new RegExp('(?<header># Description|# Team\'s Response|## State the duplicated issue ' +
110-
'here, if any|## Proposed Assignees|# Items for the Tester to Verify|# Tutor\'s Response|## Tutor to check)\\s+' +
111-
'(?<description>[\\s\\S]*?)(?=# Team\'s Response|## State the duplicated issue here, if any|## Proposed Assignees|' +
112-
'# Items for the Tester to Verify|# Tutor\'s Response|## Tutor to check|$)', 'gi');
118+
'here, if any|# Disputes)\\s+' +
119+
'(?<description>[\\s\\S]*?)(?=# Team\'s Response|## State the duplicated issue here, if any|' +
120+
'# Disputes|$)', 'gi');
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
export class TesterResponse {
2-
title: string; // e.g Change of Severity
3-
description: string; // e.g Changed from High to Low
4-
disagreeCheckbox: string; // e.g [x] I disagree
2+
readonly TITLE_PREFIX = '## :question: ';
3+
readonly DISAGREEMENT_PREFIX = '**Reason for disagreement:** ';
4+
readonly LINE_BREAK = '-------------------\n';
5+
title: string; // e.g Issue Severity
6+
description: string; // e.g Team chose `Low`. Originally `High`.
7+
disagreeCheckbox: string; // e.g - [x] I disagree
58
reasonForDiagreement: string;
69

710
constructor(title: string, description: string, disagreeCheckbox: string, reasonForDiagreement: string) {
@@ -13,11 +16,11 @@ export class TesterResponse {
1316

1417
toString(): string {
1518
let toString = '';
16-
toString += this.title + '\n\n';
19+
toString += this.TITLE_PREFIX + this.title + '\n\n';
1720
toString += this.description + '\n\n';
1821
toString += this.disagreeCheckbox + '\n\n';
19-
toString += '**Reason for disagreement:** ' + this.reasonForDiagreement + '\n\n';
20-
toString += '-------------------\n';
22+
toString += this.DISAGREEMENT_PREFIX + this.reasonForDiagreement + '\n\n';
23+
toString += this.LINE_BREAK;
2124
return toString;
2225
}
2326
}

src/app/core/services/github.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export class GithubService {
8080
mergeMap((numOfPages) => {
8181
const apiCalls = [];
8282
for (let i = 1; i <= numOfPages; i++) {
83-
apiCalls.push(from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, per_page: 1, page: i})));
83+
apiCalls.push(from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, page: i})));
8484
}
8585
return forkJoin(apiCalls);
8686
}),
@@ -222,7 +222,7 @@ export class GithubService {
222222
}
223223

224224
private getNumberOfCommentPages(issueId: number): Observable<number> {
225-
return from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, per_page: 1, page: 1})).pipe(
225+
return from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, page: 1})).pipe(
226226
map((response) => {
227227
if (!response['headers'].link) {
228228
return 1;

src/app/core/services/issue-comment.service.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {Injectable} from '@angular/core';
2-
import {Observable, of} from 'rxjs';
2+
import {Observable, of, BehaviorSubject} from 'rxjs';
33
import {GithubService} from './github.service';
44
import {IssueComment, IssueComments} from '../models/comment.model';
55
import {map} from 'rxjs/operators';
66
import * as moment from 'moment';
7+
import { TesterResponse } from '../models/tester-response.model';
8+
import { IssueDispute } from '../models/issue-dispute.model';
79

810
@Injectable({
911
providedIn: 'root',
@@ -16,11 +18,7 @@ export class IssueCommentService {
1618
}
1719

1820
getIssueComments(issueId: number): Observable<IssueComments> {
19-
if (!this.comments.get(issueId)) {
20-
return this.initializeIssueComments(issueId);
21-
} else {
22-
return of(this.comments.get(issueId));
23-
}
21+
return this.initializeIssueComments(issueId);
2422
}
2523

2624
createIssueComment(issueId: number, description: string): Observable<IssueComment> {
@@ -34,7 +32,7 @@ export class IssueCommentService {
3432
);
3533
}
3634

37-
private updateIssueComment(issueComment: IssueComment): Observable<IssueComment> {
35+
updateIssueComment(issueComment: IssueComment): Observable<IssueComment> {
3836
return this.githubService.updateIssueComment({
3937
...issueComment,
4038
description: issueComment.description,
@@ -45,6 +43,29 @@ export class IssueCommentService {
4543
);
4644
}
4745

46+
// Template url: https://github.com/CATcher-org/templates#teams-response-1
47+
createGithubTesterResponse(teamResponse: string, testerResponses: TesterResponse[]): string {
48+
return `# Team\'s Response\n${teamResponse}\n ` +
49+
`# Items for the Tester to Verify\n${this.getTesterResponsesString(testerResponses)}`;
50+
}
51+
52+
// Template url: https://github.com/CATcher-org/templates#tutor-moderation
53+
createGithubTutorResponse(issueDisputes: IssueDispute[]): string {
54+
let tutorResponseString = '# Tutor Moderation\n\n';
55+
for (const issueDispute of issueDisputes) {
56+
tutorResponseString += issueDispute.toTutorResponseString();
57+
}
58+
return tutorResponseString;
59+
}
60+
61+
private getTesterResponsesString(testerResponses: TesterResponse[]): string {
62+
let testerResponsesString = '';
63+
for (const testerResponse of testerResponses) {
64+
testerResponsesString += testerResponse.toString();
65+
}
66+
return testerResponsesString;
67+
}
68+
4869
private initializeIssueComments(issueId: number): Observable<IssueComments> {
4970
return this.githubService.fetchIssueComments(issueId).pipe(
5071
map((comments: []) => {

0 commit comments

Comments
 (0)