Getting Started with Congree and Angular
Introduction
This document is itended to give some ideas and code snippets to allow for an easier integration of Congree's features in any application based on Angular. The code snippets shown in this document are based on Angular 7 and higher.
It is not the intention of this document to provide a full-blown tutorial on Angular development but rather to give some ideas and hints on how to integrate Congree into an Angular app.
The entire functionality is being encapsulated into a single service that can be injected into any Angular component. It provides a hig-level API hiding the complexity of Congree's REST API.
Authentication
The preferred authentication method is TrustCMS. The usage is show in the code below. To be able to use this authentication method you have to make sure that you configure Congree to allow for TrustCMS authentication. Please refer to the Integration Guide and the User Rights settings in the official manuals.
import { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders, HttpParams , HttpResponse} from '@angular/common/http'; @Injectable({ providedIn: 'root' }) @Injectable() export class CongreeService { authUrl = '/congreeidentityserver/connect/token'; constructor(private http: HttpClient) { } init(){} public GetToken(serverUrl: string, username: string, password: string) { let headers = new HttpHeaders(); headers = headers.set('content-type', 'application/x-www-form-urlencoded'); let params = new HttpParams().set('grant_type', 'password'); params = params.append('congree_auth_type', 'trustcms'); params = params.append('username', username); params = params.append('password', password); params = params.append('scope', 'webApi'); params = params.append('client_id', 'CongreeWebAPI'); return this.http.post(serverUrl + this.authUrl, params, this.getAuthHeaders()); } return token; } private getArgHeaders(): any { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' }) }; return httpOptions; } }
The following listing demonstrates how this service can be injected into a component. It assumes that the corresponding HTML file contains a button firing the event 'click'.
import { Component } from '@angular/core'; import * as ClassicEditor from '@ckeditor/ckeditor5-build-classic'; import { CongreeService } from '../services/congree.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { public Editor = ClassicEditor; title = 'CongreeAngularApp'; constructor(private congreeService: CongreeService){} connect() { this.congreeService.GetToken('http://WIN-KI2KQ6TURVN', 'UserOperator', '""').subscribe((res) => { this.token = res['access_token']; this.getRules(); }); }; }
The parameters in line 20 are hard-coded. I a real-life they will be based on the user's credential and the actual server URL. Congree's REST API returns the result as an observable. The format is a JSON object like shown below. Line 20 in the code above shows how to access the returned token.
{ "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImE2MmJlNGQyNzE1MzAwN2I3Mjk2MTFkYjNiYTI1Y2IzIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1Nzc2OTMwMDksImV4cCI6MTU3NzY5NjYwOSwiaXNzIjoiaHR0cDovL3dpbi1raTJrcTZ0dXJ2bi9jb25ncmVlaWRlbnRpdHlzZXJ2ZXIiLCJhdWQiOlsiaHR0cDovL3dpbi1raTJrcTZ0dXJ2bi9jb25ncmVlaWRlbnRpdHlzZXJ2ZXIvcmVzb3VyY2VzIiwiV2ViQXBpIl0sImNsaWVudF9pZCI6IkNvbmdyZWVXZWJBUEkiLCJzdWIiOiIyNzFhNjUyMy1kOWMwLTRjMzYtOWYzNS0xZjViNmUyOGM2MTEiLCJhdXRoX3RpbWUiOjE1Nzc2OTMwMDksImlkcCI6ImxvY2FsIiwibmFtZSI6IlVzZXJPcGVyYXRvciIsInNjb3BlIjpbIndlYkFwaSJdLCJhbXIiOlsicHdkIl19.jwNobmcH6CDWK9I4JZLzw2KC2pU9maJTX2scdrXyIe--yOrSBe81_6-9zz9TOA1yr8oLq0i9ZP7KAyoZiO_nD_YdlJ-Eh9dyXI7gREDzoUd9LOK90Jxq8FksdJkPfWs9dIBw0a1ZOVLZqm8YwHLVUHoROghg5yvwDOHyD_uYMDNn6HrP4ExuPxfT85AJ9c6T1ywY8B0I9GUYASy-I-OkgmTIUxtJFvr1Z2EQ-mC_OkyVchzcj-85wd3fRnA9GWcsPmw83XooyQKWSkPJU16F2C1thb94pKiCuudcDN83ah64Mj3gUXJwK1MYqluQWl6ii8POQ-PSSg0eSI6-uasJMg", "expires_in": 3600, "token_type": "Bearer" }
Checking documents
Getting the rules
To be able to do linguistic checks we need to specify the rules the checks are based upon. The rules sets are part of any configuration of the Congree Language Server. To allow the user to select the rules he wants to apply for a check we need to obtain them from the server.
We do this by adding a new method to the service.
public GetDocumentRules(serverUrl: string, token: string) { let headers = new HttpHeaders(); headers = headers.set('Authorization', 'Bearer ' + token); return this.http.request('GET', this.serverUrl + this.documentsUrl, { headers }); }
The argument token is the token we obtained during the authentication process. The expression this.documentsUrl is a reference to a constant defined in the header of the component declaration.
documentsUrl = '/congree/api/Settings/v1/Document/';
The rest of the code is self-explanatory.
The method GetDocumentRules returns an observable that can be subscribed like shown in the next listing.
getRules() { this.congreeService.GetDocumentRules('http://WIN-KI2KQ6TURVN', this.token).subscribe((res: []) => { this.rules = res; this.rules = Array.from(this.rules) });; }
The observable is a list of strings that need to be converted to an array to be useful in UI components like the select element in HTML. The observable may look like this:
[ "Congree Standard", "Compathy_InDesign_DE", "Compathy_InDesign_EN", "Compathy_DITA", "Compathy_Office_DE", "Compathy_Office_EN", "Author it", "InDesign_Auto", "Compathy_DITA_EN", "CWI_DE", "CWI_EN", "STE", "Compathy_DITA_FR", "Compathy_InDesign_FR", "Compathy_Office_FR", "CWI_FR", "Compathy_DITA_ES", "Compathy_Office_ES", "Italian", "日本の" ]
Checking a document
A document is always a well-formed XML document. Make sure that you obey the constraints that are applicable to well-formed documents. To allow for a check of documents the service will be enhanced by the following method:
public Check(serverUrl: string,text: string, ruleSet: string, token: string) { let body = { 'Xml': text + , 'options': { "includeValidTerms": true, "includeReporting": true, "extractTermCandidates": true, "includeContextMapping": true } }; let headers = new HttpHeaders(); headers = headers.set('Authorization', 'Bearer ' + token); headers = headers.append('Content-Type', 'application/json'); return this.http.post(this.serverUrl + this.checkDocumentUrl + ruleSet, body, { headers: headers }); }
The parameters of the method Check.
Parameter | Description |
---|---|
serverUrl | the URL of the server where the Congree Api resides |
text | The document you are going to check. |
ruleSet | The name of the rule set you want to perform the check against. |
token | the bearer token you have obainted during the authentication process |
To make the method more generic you may want to add additional parameters to allow for a flexible configuration of the check and the response object. In the sample code above the configuration is hard-wired. A better approach would be the one shown in the next listing.
public Check(serverUrl: string,text: string, ruleSet: string, token: string, includeValidTerms: boolean, includeReporting: boolean, extractTermCandidates: boolean, includeContextMapping: boolean) { let body = { 'Xml': text + , 'options': { "includeValidTerms": includeReporting, "includeReporting": extractTermCandidates, "extractTermCandidates": extractTermCandidates, "includeContextMapping": includeContextMapping } }; let headers = new HttpHeaders(); headers = headers.set('Authorization', 'Bearer ' + token); headers = headers.append('Content-Type', 'application/json'); return this.http.post(this.serverUrl + this.checkDocumentUrl + ruleSet, body, { headers: headers }); }
In an Angular component you can now use this method which returns an observable.
check(text) { this.congreeService.Check(text, this.selectedRule, this.token).subscribe((res: CongreeCheckResult) => { console.log(res); }); };
Processing the server response
The response is a huge JSON object containing information about each finding as well as information about terms and reporting information. To allow for an easier access to this information in the presentation layer of your application it is a good idea to convert this JSON object into a more convient TypeScript structure. In the sample above the response is being converted into a CongreeCheckResult object representing the JSON structure.
import { CongreeError } from './CongreeError'; import { CongreeTermCandidate } from './CongreeTermCandidate'; import { CongreeReporting } from './CongreeReporting'; export interface CongreeCheckResult { ResultXml: string; Errors: CongreeError[]; TermCandidates: CongreeTermCandidate[]; Reporting: CongreeReporting; }
import { CongreeErrorDescription} from '../lib/CongreeErrorDescription'; import { CongreeProposal } from './CongreeProposal'; import { CongreeExplanation } from './CongreeExplanation'; export interface CongreeError{ Id: string; ContextId: string; Type: string; Descriptions: CongreeErrorDescription[]; Explanation: CongreeExplanation[]; ExplanationReWrite: CongreeExplanation[]; Proposals: CongreeProposal[]; }
export interface CongreeExplanation{ Type: string; Text: string; }
export interface CongreeErrorDescription{ Code: string; Description: string; Header: string; Instruction: string; Explanation: string; }
export interface CongreeProposal{ ConceptId: string; Text: string; AdditionalInfo: string; BaseTermForm: string; }
import { CongreeReportingType } from './CongreeReportingType'; export interface CongreeReporting{ TotalCheckedWords: number; ReleaseLevel: string; RelativeReleaseLevel: string; SafeReleaseLevel: string; SafeReleaseLevelTitle: string; AcceptableReleaseLevel: string; AcceptableReleaseLevelTitle: string; UnsafeReleaseLevel: string; UnsafeReleaseLevelTitle: string; ReportingByTypes: CongreeReportingType[]; }
export interface CongreeReportingType{ Type: string; ErrorCount: string; Severity: string; RelativeSeverity: string; }
export interface CongreeTermCandidate{ Id: string; Text: string; Context: string; IsExist: boolean; }
Processing the check result
This section details the response object and the way it is rendered in Congree's UIs. The sample is a part of the infamous Compathy Grasp SX document.
Checking this text results in the following JSON response.
{ "ResultXml": "<body><p><?cngr-ctx-b 1?>Congratulations on your purchase of the <?cngr-lc-b 0?>ContentGrasp SX<?cngr-lc-e 0?> <?cngr-lc-b 1?>headset<?cngr-lc-e 1?>. This revolutionary <?cngr-lc-b 2?>device<?cngr-lc-e 2?> <?cngr-lc-b 3?>will significantly improve<?cngr-lc-e 3?> the quality of your <?cngr-lc-b 4?>technical documentation<?cngr-lc-e 4?> without implementing an additional <?cngr-lc-b 5?>work step<?cngr-lc-e 5?>. During the <?cngr-lc-b 6?>normal review<?cngr-lc-e 6?>, the reviewers or <?cngr-lc-b 7?>test persons<?cngr-lc-e 7?> can <?cngr-lc-b 8?>check<?cngr-lc-e 8?><?cngr-ctx-e 1?></p><ul><li><?cngr-ctx-b 2?><?cngr-lc-b 9?>how<?cngr-lc-e 9?> <?cngr-lc-b 10?>comprehensible individual passages<?cngr-lc-e 10?> were<?cngr-lc-b 11?>;<?cngr-lc-e 11?><?cngr-ctx-e 2?></li><li><?cngr-ctx-b 3?><?cngr-lc-b 12?>how<?cngr-lc-e 12?> carefully <?cngr-lc-b 13?>individual passages<?cngr-lc-e 13?> <?cngr-lc-b 14?>were read<?cngr-lc-e 14?><?cngr-lc-b 15?>;<?cngr-lc-e 15?><?cngr-ctx-e 3?></li><li><?cngr-ctx-b 4?><?cngr-lc-b 16?>which<?cngr-lc-e 16?> passages needed to be read repeatedly.<?cngr-ctx-e 4?></li></ul><p><?cngr-ctx-b 5?>This information <?cngr-lc-b 17?>is recorded<?cngr-lc-e 17?> by the <?cngr-lc-b 18?>headset<?cngr-lc-e 18?> and analyzed by <?cngr-lc-b 19?>intelligent software<?cngr-lc-e 19?> <?cngr-lc-b 20?>using<?cngr-lc-e 20?> a <?cngr-lc-b 21?>sophisticated algorithm<?cngr-lc-e 21?>.<?cngr-ctx-e 5?></p><p><?cngr-ctx-b 6?>The result enables detailed, <?cngr-lc-b 22?>systematic <?cngr-lc-b 23?>improvement<?cngr-lc-e 23?> of the comprehensibility of <?cngr-lc-b 24?>technical document<?cngr-lc-e 22?><?cngr-lc-e 24?> at an <?cngr-lc-b 25?>unprecedented level<?cngr-lc-e 25?>.<?cngr-ctx-e 6?></p></body>", "Errors": [ { "Id": "0", "ContextId": 1, "Type": "Terminology", "Descriptions": [ { "Code": "DEPR", "Description": { "Header": "Review this term.", "Instruction": "Replace with the corresponding preferred term.", "Explanation": "The status of this term in term database is 'deprecated'." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "Compathy ContentGrasp SX", "AdditionalInfo": null, "BaseTermForm": "ContentGrasp SX", "ConceptId": 1 } ] }, { "Id": "1", "ContextId": 1, "Type": "ValidTerm", "Descriptions": [], "Proposals": [ { "Text": "headset", "AdditionalInfo": null, "BaseTermForm": "headset", "ConceptId": 10 } ] }, { "Id": "2", "ContextId": 1, "Type": "Terminology", "Descriptions": [ { "Code": "ADM", "Description": { "Header": "Review this term.", "Instruction": "Check whether it is better to use the corresponding preferred term.", "Explanation": "The status of this term in term database is 'admitted'." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "instrument", "AdditionalInfo": null, "BaseTermForm": "device", "ConceptId": 7 } ] }, { "Id": "3", "ContextId": 1, "Type": "Style", "Descriptions": [ { "Code": "s752en", "Description": { "Header": "Review the writing style.", "Instruction": "Avoid the future tense. Use the present tense if appropriate.", "Explanation": "The future tense is not appropriate in technical documentation." }, "Explanation": [ { "Type": "Text", "Text": "If you attempt to copy a directory without using this option, you " }, { "Type": "Explanation", "Text": "will receive" }, { "Type": "Text", "Text": " an error message." } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "If you attempt to copy a directory without using this option, you " }, { "Type": "Explanation", "Text": "receive" }, { "Type": "Text", "Text": " an error message." } ] } ], "Proposals": [] }, { "Id": "8", "ContextId": 1, "Type": "Grammar", "Descriptions": [ { "Code": "g523en", "Description": { "Header": "Review punctuation.", "Instruction": "Insert a punctuation mark such as '.', '!', '?', ':' if appropriate.", "Explanation": "Sentences end with a punctuation mark." }, "Explanation": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end" } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end." } ] } ], "Proposals": [] }, { "Id": "9", "ContextId": 2, "Type": "Spelling", "Descriptions": [ { "Code": "o43en", "Description": { "Header": "Review the spelling of this word.", "Instruction": "Capitalize the word.", "Explanation": "Words at the beginning of sentences start with a capital letter." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "How", "AdditionalInfo": null, "BaseTermForm": null, "ConceptId": 0 } ] }, { "Id": "11", "ContextId": 2, "Type": "Grammar", "Descriptions": [ { "Code": "g523en", "Description": { "Header": "Review punctuation.", "Instruction": "Insert a punctuation mark such as '.', '!', '?', ':' if appropriate.", "Explanation": "Sentences end with a punctuation mark." }, "Explanation": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end" } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end." } ] } ], "Proposals": [] }, { "Id": "12", "ContextId": 3, "Type": "Spelling", "Descriptions": [ { "Code": "o43en", "Description": { "Header": "Review the spelling of this word.", "Instruction": "Capitalize the word.", "Explanation": "Words at the beginning of sentences start with a capital letter." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "How", "AdditionalInfo": null, "BaseTermForm": null, "ConceptId": 0 } ] }, { "Id": "14", "ContextId": 3, "Type": "Style", "Descriptions": [ { "Code": "s712en", "Description": { "Header": "Review the writing style.", "Instruction": "This construction uses the passive voice. Consider using the active voice.", "Explanation": "The passive voice emphasizes the result and often obscures the initiator of the action. The active voice reinforces a cause/effect relationship between the initiator, the action and the result." }, "Explanation": [ { "Type": "Text", "Text": "After the tap " }, { "Type": "Explanation", "Text": "has been turned off" }, { "Type": "Text", "Text": ", the shaft will move." } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "After " }, { "Type": "Explanation", "Text": "you have turned the tap off" }, { "Type": "Text", "Text": ", the shaft will move." } ] } ], "Proposals": [] }, { "Id": "15", "ContextId": 3, "Type": "Grammar", "Descriptions": [ { "Code": "g523en", "Description": { "Header": "Review punctuation.", "Instruction": "Insert a punctuation mark such as '.', '!', '?', ':' if appropriate.", "Explanation": "Sentences end with a punctuation mark." }, "Explanation": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end" } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "Sentences have punctuation marks at the " }, { "Type": "Explanation", "Text": "end." } ] } ], "Proposals": [] }, { "Id": "16", "ContextId": 4, "Type": "Spelling", "Descriptions": [ { "Code": "o43en", "Description": { "Header": "Review the spelling of this word.", "Instruction": "Capitalize the word.", "Explanation": "Words at the beginning of sentences start with a capital letter." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "Which", "AdditionalInfo": null, "BaseTermForm": null, "ConceptId": 0 } ] }, { "Id": "17", "ContextId": 5, "Type": "Style", "Descriptions": [ { "Code": "s712en", "Description": { "Header": "Review the writing style.", "Instruction": "This construction uses the passive voice. Consider using the active voice.", "Explanation": "The passive voice emphasizes the result and often obscures the initiator of the action. The active voice reinforces a cause/effect relationship between the initiator, the action and the result." }, "Explanation": [ { "Type": "Text", "Text": "After the tap " }, { "Type": "Explanation", "Text": "has been turned off" }, { "Type": "Text", "Text": ", the shaft will move." } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "After " }, { "Type": "Explanation", "Text": "you have turned the tap off" }, { "Type": "Text", "Text": ", the shaft will move." } ] } ], "Proposals": [] }, { "Id": "18", "ContextId": 5, "Type": "ValidTerm", "Descriptions": [], "Proposals": [ { "Text": "headset", "AdditionalInfo": null, "BaseTermForm": "headset", "ConceptId": 10 } ] }, { "Id": "20", "ContextId": 5, "Type": "Style", "Descriptions": [ { "Code": "s243en", "Description": { "Header": "Check for ambiguous structures and unclear relations.", "Instruction": "Consider rewriting the '-ing'-construction.", "Explanation": "The sentence may be ambiguous. The '-ing'-form can pose a problem for translation." }, "Explanation": [ { "Type": "Text", "Text": "1) " }, { "Type": "Explanation", "Text": "Disabling" }, { "Type": "Text", "Text": " network services prevents IP packets from doing any harm to the system.\\n\\n2) The Document Editor sends an edit message request " }, { "Type": "Explanation", "Text": "using" }, { "Type": "Text", "Text": " the file name as a parameter for the message." } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "1) " }, { "Type": "Explanation", "Text": "The disabling of" }, { "Type": "Text", "Text": " network services prevents IP packets from doing any harm to the system.\\nOR:\\n" }, { "Type": "Explanation", "Text": "If you disable" }, { "Type": "Text", "Text": " the network services, the IP packets do not do any harm to the system.\\n\\n2) The Document Editor sends an edit message request " }, { "Type": "Explanation", "Text": "that uses" }, { "Type": "Text", "Text": " the file name as a parameter for the message.\\nOR:\\nThe Document Editor sends an edit message request " }, { "Type": "Explanation", "Text": "by using" }, { "Type": "Text", "Text": " the file name as a parameter for the message." } ] } ], "Proposals": [] }, { "Id": "22", "ContextId": 6, "Type": "Style", "Descriptions": [ { "Code": "s221en", "Description": { "Header": "Check for ambiguous structures and unclear relations.", "Instruction": "Avoid more than one noun modifier with 'of'. Rephrase the sentence if possible.", "Explanation": "It might be unclear what is modified by the second 'of'-phrase." }, "Explanation": [ { "Type": "Text", "Text": "The " }, { "Type": "Explanation", "Text": "invitation of the members of the committee" }, { "Type": "Text", "Text": " was sent out last Friday." } ], "ExplanationRewrite": [ { "Type": "Explanation", "Text": "The invitation of the committee members" }, { "Type": "Text", "Text": " was sent out last Friday." } ] } ], "Proposals": [] }, { "Id": "23", "ContextId": 6, "Type": "Terminology", "Descriptions": [ { "Code": "POSNEG", "Description": { "Header": "Review this term.", "Instruction": "Check whether in this context the corresponding preferred term is the better choice.", "Explanation": "This is a deprecated term and it is also a preferred or admitted term." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "improvement", "AdditionalInfo": null, "BaseTermForm": "improvement", "ConceptId": 11 }, { "Text": "upgrade", "AdditionalInfo": null, "BaseTermForm": "improvement", "ConceptId": 12 } ] } ], "TermCandidates": [ { "Id": "4", "Text": "technical documentation", "Context": "Congratulations on your purchase of the ContentGrasp SX headset. This revolutionary device will significantly improve the quality of your technical documentation without implementing an additional work step. During the normal review, the reviewers or test persons can check", "IsExist": false }, { "Id": "5", "Text": "work step", "Context": "Congratulations on your purchase of the ContentGrasp SX headset. This revolutionary device will significantly improve the quality of your technical documentation without implementing an additional work step. During the normal review, the reviewers or test persons can check", "IsExist": false }, { "Id": "6", "Text": "normal review", "Context": "Congratulations on your purchase of the ContentGrasp SX headset. This revolutionary device will significantly improve the quality of your technical documentation without implementing an additional work step. During the normal review, the reviewers or test persons can check", "IsExist": false }, { "Id": "7", "Text": "test person", "Context": "Congratulations on your purchase of the ContentGrasp SX headset. This revolutionary device will significantly improve the quality of your technical documentation without implementing an additional work step. During the normal review, the reviewers or test persons can check", "IsExist": false }, { "Id": "10", "Text": "comprehensible individual passage", "Context": "how comprehensible individual passages were;", "IsExist": false }, { "Id": "13", "Text": "individual passage", "Context": "how carefully individual passages were read;", "IsExist": false }, { "Id": "19", "Text": "intelligent software", "Context": "This information is recorded by the headset and analyzed by intelligent software using a sophisticated algorithm.", "IsExist": false }, { "Id": "21", "Text": "sophisticated algorithm", "Context": "This information is recorded by the headset and analyzed by intelligent software using a sophisticated algorithm.", "IsExist": false }, { "Id": "24", "Text": "technical document", "Context": "The result enables detailed, systematic improvement of the comprehensibility of technical document at an unprecedented level.", "IsExist": false }, { "Id": "25", "Text": "unprecedented level", "Context": "The result enables detailed, systematic improvement of the comprehensibility of technical document at an unprecedented level.", "IsExist": false } ], "Reporting": { "TotalCheckedWords": 106, "ReleaseLevel": 0, "RelativeReleaseLevel": 0, "SafeReleaseLevel": 95, "SafeReleaseLevelTitle": "Safe", "AcceptableReleaseLevel": 85, "AcceptableReleaseLevelTitle": "Acceptable", "UnsafeReleaseLevel": 0, "UnsafeReleaseLevelTitle": "Unsafe", "ReportingByTypes": [ { "Type": "Abbreviation", "ErrorCount": 0, "Severity": 100, "RelativeSeverity": 120 }, { "Type": "Grammar", "ErrorCount": 3, "Severity": 91.51, "RelativeSeverity": 66.04000000000002 }, { "Type": "Spelling", "ErrorCount": 3, "Severity": 71.7, "RelativeSeverity": 33.741176470588236 }, { "Type": "Style", "ErrorCount": 5, "Severity": 95.28, "RelativeSeverity": 82.24000000000001 }, { "Type": "Terminology", "ErrorCount": 3, "Severity": 5.66, "RelativeSeverity": 2.663529411764706 } ] }, "FailedParagraphsInfo": { "FailedParagraphsAmount": 0 } }
If there are any findings they are returned within the arry of Errors. Each error is being rendered as a separated item in the UI. Let's analyze the first error object in the JSON response.
{ "Id": "0", "ContextId": 1, "Type": "Terminology", "Descriptions": [ { "Code": "DEPR", "Description": { "Header": "Review this term.", "Instruction": "Replace with the corresponding preferred term.", "Explanation": "The status of this term in term database is 'deprecated'." }, "Explanation": [], "ExplanationRewrite": [] } ], "Proposals": [ { "Text": "Compathy ContentGrasp SX", "AdditionalInfo": null, "BaseTermForm": "ContentGrasp SX", "ConceptId": 1 } ] }
The information in the JSON response is being rendered like shown below.
The following example is a more comprehensive one containing additional information explaining the issue and providing hints to rewrite the sentence.
{ "Id": "17", "ContextId": 5, "Type": "Style", "Descriptions": [ { "Code": "s712en", "Description": { "Header": "Review the writing style.", "Instruction": "This construction uses the passive voice. Consider using the active voice.", "Explanation": "The passive voice emphasizes the result and often obscures the initiator of the action. The active voice reinforces a cause/effect relationship between the initiator, the action and the result." }, "Explanation": [ { "Type": "Text", "Text": "After the tap " }, { "Type": "Explanation", "Text": "has been turned off" }, { "Type": "Text", "Text": ", the shaft will move." } ], "ExplanationRewrite": [ { "Type": "Text", "Text": "After " }, { "Type": "Explanation", "Text": "you have turned the tap off" }, { "Type": "Text", "Text": ", the shaft will move." } ] } ], "Proposals": [] }
Below you see the rendered presentation of this JSON object.
Finding terms
Finding a term is straightforward. The Angular service gets a new method that does all the job for us:
public SearchTerm(serverUrl: string, term: string, ruleSet: string, token: string){ let headers = new HttpHeaders(); headers = headers.set('Authorization', 'Bearer ' + token); return this.http.request('GET', this.serverUrl + this.termUrl + 'term=' + term + '&ruleSet=' + ruleSet, {headers}); }
Parameter | Description |
---|---|
serverUrl | the URL of the host where the Congree REST API resides |
term | The term we are looking for |
ruleSet | The rule set we want to use |
token | The authentication token |
The variable termUrl is defined in the header of the service.
termUrl ='/congree/api/Terminology/v1/SearchTerm?';
Getting information on rules sets
A couple of the REST API endpoint expect different parameters that are related to properties of rules sets. In Checking documents we have learned how to retrieve available rule sets and how to use them to perform a document check. However, a rule set is defined by more properties than just the name.
Let's see how we can retrieve all the properties belonging to a rule set. To do so we enhance the service file by this method:
public GetRuleSetSttings( ruleSet: string, token: string) { let headers = new HttpHeaders(); headers = headers.set('Authorization', 'Bearer ' + token); return this.http.request('GET', this.documentSettingsUrl + ruleSet, { headers }); }
The variable documentSettingsUrl must be defined in the header part of the service.
documentSettingsUrl = '/congree/api/Settings/v1/Document/';
Parameter | Description |
---|---|
ruleSet | The name of the rule set. |
token | The bearer token to do the authentication |
We can now use this method in one of our Angular components.
getRulesSettings(ruleSet:string){ this.congreeService.GetRuleSetSttings(ruleSet,this.token).subscribe((res: CongreeRulesSetConfiguration) => { console.log(res); }); }
The response is an obervable that we have to subscribe. To make the access to and the processing of this structure more convenient the sample above use an automatic conversion into a JavaScript structure shown below.
export interface CongreeRulesSetConfiguration{ LanguageSettings: { Language: string }; LinguisticReportingConfiguration:{ ConfigurationName: string; Language: string, WordCountFactor: string, HighRatingLimit: string, HighRatingName: string, MediumRatingLimit: string, MediumRatingName: string, LowRatingLimit: string, LowRatingName: string, Rules:[ { RuleName:string, CategoryType:string, Weight:string } ], Categories:[ { ErrorType:string, Priority: string } ] }; ParagraphRecognition:{ IsAutomaticMode: boolean, SymbolsAmountToCheck: string, CheckUnitDefinitions:[], InlineElementDefinitions:[] }; TermCandidates:{ Attributes:[{ AttributeLevel: string, Type: string, AttributeName: string, Values:[], AutoValue:string, Editable: boolean, Mandatory: false } ] } }
If we want to use the language information which is being refered to in the API documentation as culture we can do so like shown below.
getRulesSettings(ruleSet:string){ this.congreeService.GetRuleSetSttings(ruleSet,this.token).subscribe((res: CongreeRulesSetConfiguration) => { console.log(res.LanguageSettings.Language); }); }