Skip to content

Commit

Permalink
Tester Response and Moderation Phase changes (#151)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
KevinCJH authored Aug 2, 2019
1 parent a97674a commit 326111a
Show file tree
Hide file tree
Showing 26 changed files with 643 additions and 103 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "CATcher",
"version": "1.0.0",
"version": "3.1.0",
"main": "main.js",
"scripts": {
"postinstall": "npm run postinstall:electron && electron-builder install-app-deps",
Expand Down
38 changes: 38 additions & 0 deletions src/app/core/models/issue-dispute.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export class IssueDispute {
readonly TODO_UNCHECKED = '- [ ] Done';
readonly INITIAL_RESPONSE = '[replace this with your explanation]';
readonly TITLE_PREFIX = '## :question: ';
readonly LINE_BREAK = '-------------------\n';
title: string; // e.g Issue severity
description: string; // e.g Team says: xxx\n Tester says: xxx.
tutorResponse: string; // e.g Not justified. I've changed it back.
todo: string; // e.g - [x] Done

constructor(title: string, description: string) {
this.title = title;
this.description = description;
this.tutorResponse = this.INITIAL_RESPONSE;
this.todo = this.TODO_UNCHECKED;
}

/*
This method is used to format the tutor's response so that the app can upload it on Github.
Refer to format in https://github.com/CATcher-org/templates#app-collect-tutor-response
*/
toTutorResponseString(): string {
let toString = '';
toString += this.TITLE_PREFIX + this.title + '\n\n';
toString += this.todo + '\n\n';
toString += this.tutorResponse + '\n\n';
toString += this.LINE_BREAK;
return toString;
}

toString(): string {
let toString = '';
toString += this.TITLE_PREFIX + this.title + '\n\n';
toString += this.description + '\n\n';
toString += this.LINE_BREAK;
return toString;
}
}
16 changes: 12 additions & 4 deletions src/app/core/models/issue.model.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {Team} from './team.model';
import { TesterResponse } from './tester-response.model';
import { IssueComment, IssueComments } from './comment.model';
import { IssueDispute } from './issue-dispute.model';

export interface Issue {
readonly id: number;
Expand All @@ -18,6 +20,10 @@ export interface Issue {
teamResponse?: string;
tutorResponse?: string;
testerResponses?: TesterResponse[];
issueComment?: IssueComment; // Issue comment is used for Tutor Response and Tester Response
issueDisputes?: IssueDispute[];
pending?: string;
unsure?: boolean;
}

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

export const labelsToAttributeMapping = {
'severity': 'severity',
'type': 'type',
'response': 'responseTag',
'status': 'status',
'pending': 'pending',
'unsure': 'unsure'
};

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

export const phaseModerationDescriptionTemplate = new RegExp('(?<header># Description|# Team\'s Response|## State the duplicated issue ' +
'here, if any|## Proposed Assignees|# Items for the Tester to Verify|# Tutor\'s Response|## Tutor to check)\\s+' +
'(?<description>[\\s\\S]*?)(?=# Team\'s Response|## State the duplicated issue here, if any|## Proposed Assignees|' +
'# Items for the Tester to Verify|# Tutor\'s Response|## Tutor to check|$)', 'gi');
'here, if any|# Disputes)\\s+' +
'(?<description>[\\s\\S]*?)(?=# Team\'s Response|## State the duplicated issue here, if any|' +
'# Disputes|$)', 'gi');
15 changes: 9 additions & 6 deletions src/app/core/models/tester-response.model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
export class TesterResponse {
title: string; // e.g Change of Severity
description: string; // e.g Changed from High to Low
disagreeCheckbox: string; // e.g [x] I disagree
readonly TITLE_PREFIX = '## :question: ';
readonly DISAGREEMENT_PREFIX = '**Reason for disagreement:** ';
readonly LINE_BREAK = '-------------------\n';
title: string; // e.g Issue Severity
description: string; // e.g Team chose `Low`. Originally `High`.
disagreeCheckbox: string; // e.g - [x] I disagree
reasonForDiagreement: string;

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

toString(): string {
let toString = '';
toString += this.title + '\n\n';
toString += this.TITLE_PREFIX + this.title + '\n\n';
toString += this.description + '\n\n';
toString += this.disagreeCheckbox + '\n\n';
toString += '**Reason for disagreement:** ' + this.reasonForDiagreement + '\n\n';
toString += '-------------------\n';
toString += this.DISAGREEMENT_PREFIX + this.reasonForDiagreement + '\n\n';
toString += this.LINE_BREAK;
return toString;
}
}
4 changes: 2 additions & 2 deletions src/app/core/services/github.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export class GithubService {
mergeMap((numOfPages) => {
const apiCalls = [];
for (let i = 1; i <= numOfPages; i++) {
apiCalls.push(from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, per_page: 1, page: i})));
apiCalls.push(from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, page: i})));
}
return forkJoin(apiCalls);
}),
Expand Down Expand Up @@ -222,7 +222,7 @@ export class GithubService {
}

private getNumberOfCommentPages(issueId: number): Observable<number> {
return from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, per_page: 1, page: 1})).pipe(
return from(octokit.issues.listComments({owner: ORG_NAME, repo: REPO, number: issueId, page: 1})).pipe(
map((response) => {
if (!response['headers'].link) {
return 1;
Expand Down
35 changes: 28 additions & 7 deletions src/app/core/services/issue-comment.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {Observable, of, BehaviorSubject} from 'rxjs';
import {GithubService} from './github.service';
import {IssueComment, IssueComments} from '../models/comment.model';
import {map} from 'rxjs/operators';
import * as moment from 'moment';
import { TesterResponse } from '../models/tester-response.model';
import { IssueDispute } from '../models/issue-dispute.model';

@Injectable({
providedIn: 'root',
Expand All @@ -16,11 +18,7 @@ export class IssueCommentService {
}

getIssueComments(issueId: number): Observable<IssueComments> {
if (!this.comments.get(issueId)) {
return this.initializeIssueComments(issueId);
} else {
return of(this.comments.get(issueId));
}
return this.initializeIssueComments(issueId);
}

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

private updateIssueComment(issueComment: IssueComment): Observable<IssueComment> {
updateIssueComment(issueComment: IssueComment): Observable<IssueComment> {
return this.githubService.updateIssueComment({
...issueComment,
description: issueComment.description,
Expand All @@ -45,6 +43,29 @@ export class IssueCommentService {
);
}

// Template url: https://github.com/CATcher-org/templates#teams-response-1
createGithubTesterResponse(teamResponse: string, testerResponses: TesterResponse[]): string {
return `# Team\'s Response\n${teamResponse}\n ` +
`# Items for the Tester to Verify\n${this.getTesterResponsesString(testerResponses)}`;
}

// Template url: https://github.com/CATcher-org/templates#tutor-moderation
createGithubTutorResponse(issueDisputes: IssueDispute[]): string {
let tutorResponseString = '# Tutor Moderation\n\n';
for (const issueDispute of issueDisputes) {
tutorResponseString += issueDispute.toTutorResponseString();
}
return tutorResponseString;
}

private getTesterResponsesString(testerResponses: TesterResponse[]): string {
let testerResponsesString = '';
for (const testerResponse of testerResponses) {
testerResponsesString += testerResponse.toString();
}
return testerResponsesString;
}

private initializeIssueComments(issueId: number): Observable<IssueComments> {
return this.githubService.fetchIssueComments(issueId).pipe(
map((comments: []) => {
Expand Down
Loading

0 comments on commit 326111a

Please sign in to comment.