Implementing Advanced Search Feature In Susper

Susper has been provided ‘Advanced Search’ feature which provides the user a great experience to search for desired results. Advanced search has been implemented in such a way it shows top authors, top providers, and distribution regarding protocols. Users can choose any of these options to get best results. We receive data of each facet name from Yacy using yacy search endpoint. More about yacy search endpoint can be found here:  http://yacy.searchlab.eu/solr/select?query=india&fl=last_modified&start=0&rows=15&facet=true&facet.mincount=1&facet.field=host_s&facet.field=url_protocol_s&facet.field=author_sxt&facet.field=collection_sxt&wt=yjson For implementing this feature, we created Actions and Reducers using concepts of Redux. The implemented actions can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/actions/search.ts Actions have been implemented because these actually represent some kind of event. For e.g. like the beginning of an API call here. We also have created an interface for search action which can be found here under reducers as filename index.ts: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/index.ts Reducers are a pure type of function that takes the previous state and an action and returns the next state. We have used Redux to implement actions and reducers for the advanced search. For advanced search, the reducer file can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/reducers/search.ts The main logic has been implemented under advancedsearch.component.ts: export class AdvancedsearchComponent implements OnInit {   querylook = {}; // array of urls   navigation$: Observable<any>;   selectedelements: Array<any> = []; // selected urls by user changeurl(modifier, element) { // based on query urls are fetched // if an url is selected by user, it is decoded   this.querylook['query'] = this.querylook['query'] + '+' + decodeURIComponent(modifier);   this.selectedelements.push(element); // according to selected urls // results are loaded from yacy   this.route.navigate(['/search'], {queryParams: this.querylook}); } // same method is implemented for removing an url removeurl(modifier) {   this.querylook['query'] = this.querylook['query'].replace('+' + decodeURIComponent(modifier), '');   this.route.navigate(['/search'], {queryParams: this.querylook}); }   The changeurl() function replaces the query with a query and selected URL and searches for the results only from the URL provider. The removeurl() function removes URL from the query and works as a normal search, searching for the results from all providers. The source code for the implementation of advanced search feature can be found here: https://github.com/fossasia/susper.com/tree/master/src/app/advancedsearch Resources Using Postman for analyzing an endpoint of an API: https://www.getpostman.com/docs Redux documentation: http://redux.js.org/docs/introduction/

Continue ReadingImplementing Advanced Search Feature In Susper

Avoiding Nested Callbacks using RxJS in Loklak Scraper JS

Loklak Scraper JS, as suggested by the name, is a set of scrapers for social media websites written in NodeJS. One of the most common requirement while scraping is, there is a parent webpage which provides links for related child webpages. And the required data that needs to be scraped is present in both parent webpage and child webpages. For example, let’s say we want to scrape quora user profiles matching search query “Siddhant”. The matching profiles webpage for this example will be https://www.quora.com/search?q=Siddhant&type=profile which is the parent webpage, and the child webpages are links of each matched profiles. Now, a simplistic approach is to first obtain the HTML of parent webpage and then synchronously fetch the HTML of child webpages and parse them to get the desired data. The problem with this approach is that, it is slower as it is synchronous. A different approach can be using request-promise-native to implement the logic in asynchronous way. But, there are limitations with this approach. The HTML of child webpages that needs to be fetched can only be obtained after HTML of parent webpage is obtained and number of child webpages are dynamic. So, there is a request dependency between parent and child i.e. if only we have data from parent webpage we can extract data from child webpages. The code would look like this request(parent_url) .then(data => { ... request(child_url) .then(data => { // again nesting of child urls }) .catch(error => { }); }) .catch(error => { });   Firstly, with this approach there is callback hell. Horrible, isn’t it? And then we don’t know how many nested callbacks to use as the number of child webpages are dynamic. The saviour: RxJS The solution to our problem is reactive extensions in JavaScript. Using rxjs we can obtain the required data without callback hell and asynchronously! The promise-request object of the parent webpage is obtained. Using this promise-request object an observable is generated by using Rx.Observable.fromPromise. flatmap operator is used to parse the HTML of the parent webpage and obtain the links of child webpages. Then map method is used transform the links to promise-request objects which are again transformed into observables. The returned value - HTML - from the resulting observables is parsed and accumulated using zip operator. Finally, the accumulated data is subscribed. This is implemented in getScrapedData method of Quora JS scraper. getScrapedData(query, callback) { // observable from parent webpage Rx.Observable.fromPromise(this.getSearchQueryPromise(query)) .flatMap((t, i) => { // t is html of parent webpage // request-promise object of child webpages let profileLinkPromises = this.getProfileLinkPromises(t); // request-promise object to observable transformation let obs = profileLinkPromises.map(elem => Rx.Observable.fromPromise(elem)); // each Quora profile is parsed return Rx.Observable.zip( // accumulation of data from child webpages ...obs, (...profileLinkObservables) => { let scrapedProfiles = []; for (let i = 0; i < profileLinkObservables.length; i++) { let $ = cheerio.load(profileLinkObservables[i]); scrapedProfiles.push(this.scrape($)); } return scrapedProfiles; // accumulated data returned } ) }) .subscribe( // desired data is subscribed scrapedData => callback({profiles: scrapedData}), error => callback(error) ); }…

Continue ReadingAvoiding Nested Callbacks using RxJS in Loklak Scraper JS

Implementation of Text-To-Speech Feature In Susper

Susper has been given a voice search feature through which it provides the user a better experience of search. We introduced to enhance the speech recognition by adding Speech Synthesis or Text-To-Speech feature. The speech synthesis feature should only work when a voice search is attempted. The idea was to create speech synthesis similar to market leader. Here is the link to YouTube video showing the demo of the feature: Video link In the video, it will show demo : If a manual search is used then the feature should not work. If voice search is used then the feature should work. For implementing this feature, we used Speech Synthesis API which is provided with Google Chrome browser 33 and above versions. window.speechSynthesis.speak(‘Hello world!’); can be used to check whether the browser supports this feature or not. First, we created an interface: interface IWindow extends Window {   SpeechSynthesisUtterance: any;   speechSynthesis: any; };   Then under @Injectable we created a class for the SpeechSynthesisService. export class SpeechSynthesisService {   utterence: any;  constructor(private zone: NgZone) { }  speak(text: string): void {   const { SpeechSynthesisUtterance }: IWindow = <IWindow>window;   const { speechSynthesis }: IWindow = <IWindow>window;  this.utterence = new SpeechSynthesisUtterance();   this.utterence.text = text; // utters text   this.utterence.lang = 'en-US'; // default language   this.utterence.volume = 1; // it can be set between 0 and 1   this.utterence.rate = 1; // it can be set between 0 and 1   this.utterence.pitch = 1; // it can be set between 0 and 1  (window as any).speechSynthesis.speak(this.utterence); }// to pause the queue of utterence pause(): void {   const { speechSynthesis }: IWindow = <IWindow>window; const { SpeechSynthesisUtterance }: IWindow = <IWindow>window;  this.utterence = new SpeechSynthesisUtterance();   (window as any).speechSynthesis.pause();  } }   The above code will implement the feature Text-To-Speech. The source code for the implementation can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/speech-synthesis.service.ts We call speech synthesis only when voice search mode is activated. Here we used redux to check whether the mode is ‘speech’ or not. When the mode is ‘speech’ then it should utter the description inside the infobox. We did the following changes in infobox.component.ts: import { SpeechSynthesisService } from ‘../speech-synthesis.service’; speechMode: any; constructor(private synthesis: SpeechSynthesisService) { } this.query$ = store.select(fromRoot.getwholequery); this.query$.subscribe(query => {   this.keyword = query.query;   this.speechMode = query.mode; });   And we added a conditional statement to check whether mode is ‘speech’ or not. // conditional statement // to check if mode is ‘speech’ or not if (this.speechMode === ‘speech’) {   this.startSpeaking(this.results[0].description); } startSpeaking(description) {   this.synthesis.speak(description);   this.synthesis.pause(); } The source code for the implementation can be found here: https://github.com/fossasia/susper.com/commit/3624d504c4687c227016b4fea229c680ad80a613 Resources Getting started with the Speech Synthesis API. Web Apps that talk - Introduction to Speech Synthesis API.      

Continue ReadingImplementation of Text-To-Speech Feature In Susper

Adding Transition Effect Using RxJS And CSS In Voice Search UI Of Susper

  Susper has been given a voice search feature through which it provides a user with a better experience of search. We introduced to enhance the speech-recognition user interface by adding transition effects. The transition effect was required to display appropriate messages according to voice being detected or not. The following messages were: When a user should start a voice search, it should display ‘Speak Now’ message for 1-2 seconds and then show up with message ‘Listening...’ to acknowledge user that now it is ready to recognize the voice which will be spoken. If a user should do not speak anything, it should display ‘Please check audio levels or your microphone working’ message in 3-4 seconds and should exit the voice search interface. The idea of speech UI was taken from the market leader and it was implemented in a similar way. On the homepage, it looks like this: On the results page, it looks like this: For creating transitions like, ‘Listening...’ and ‘Please check audio levels and microphone’ messages, we used CSS, RxJS Observables and timer() function. Let’s start with RxJS Observables and timer() function. RxJS Observables and timer() timer() is used to emit numbers in sequence in every specified duration or after a given duration. It acts as an observable. For example: let countdown = Observable.timer(2000); The above code will emit value of countdown in 2000 milliseconds. Similarly, let’s see another example: let countdown = Observable.timer(2000, 6000); The above code will emit value of countdown in 2000 milliseconds and subsequent values in every 6000 milliseconds. export class SpeechToTextComponent implements OnInit {   message: any = 'Speak Now';   timer: any;   subscription: any;   ticks: any;   miccolor: any = ‘#f44’; } ngOnInit() {   this.timer = Observable.timer(1500, 2000);   this.subscription = this.timer.subscribe(t => {   this.ticks = t;// it will throw listening message after 1.5   sec   if (t === 1) {     this.message = “Listening...”;   }// subsequent events will be performed in 2 secs interval   // as it has been defined in timer()   if (t === 4) {     this.message = “Please check your microphone audio levels.”;     this.miccolor = ‘#C2C2C2’; }// if no voice is given, it will throw audio level message // and unsubscribe to the event to exit back on homepage   if (t === 6) {     this.subscription.unsubscribe();     this.store.dispatch(new speechactions.SearchAction(false));   }  }); } The above code will throw following messages at a particular time. For creating the text-animation effect, most developers go for plain javascript. The text-animation effects can also be achieved by using pure CSS. Text animation using CSS @-webkit-keyframes typing {from {width:0;}} .spch {   font-weight: normal;   line-height: 1.2;   pointer-events: none;   position: none;   text-align: left;   -webkit-font-smoothing: antialiased;   transition: opacity .1s ease-in, margin-left .5s ease-in,                  top  0s linear 0.218s;   -webkit-animation: typing 2s steps(21,end), blink-caret .5s                       step-end infinite…

Continue ReadingAdding Transition Effect Using RxJS And CSS In Voice Search UI Of Susper

Managing States in SUSI MagicMirror Module

SUSI MagicMirror Module is a module for MagicMirror project by which you can use SUSI directly on MagicMirror. While developing the module, a problem I faced was that we need to manage the flow between the various stages of processing of voice input by the user and displaying SUSI output to the user. This was solved by making state management flow between various states of SUSI MagicMirror Module namely, Idle State: When SUSI MagicMirror Module is actively listening for a hotword. Listening State: In this state, the user’s speech input from the microphone is recorded to a file. Busy State: The user has finished speaking or timed out. Now, we need to transcribe the audio spoken by the user, send the response to SUSI server and speak out the SUSI response. The flow between these states can be explained by the following diagram: As clear from the above diagram, transitions are not possible from a state to all other states. Only some transitions are allowed. Thus, we need a mechanism to guarantee only allowed transitions and ensure it triggers on the right time. For achieving this, we first implement an abstract class State with common properties of a state. We store the information whether a state can transition into some other state in a map allowedTransitions which maps state names “idle”, “listening” and “busy” to their corresponding states. The transition method to transition from one state to another is implemented in the following way. protected transition(state: State): void { if (!this.canTransition(state)) { console.error(`Invalid transition to state: ${state}`); return; } this.onExit(); state.onEnter(); } private canTransition(state: State): boolean { return this.allowedStateTransitions.has(state.name); } Here we first check if a transition is valid. Then we exit one state and enter into the supplied state.  We also define a state machine that initializes the default state of the Mirror and define valid transitions for each state. Here is the constructor for state machine. constructor(components: IStateMachineComponents) { this.idleState = new IdleState(components); this.listeningState = new ListeningState(components); this.busyState = new BusyState(components); this.idleState.AllowedStateTransitions = new Map<StateName, State>([["listening", this.listeningState]]); this.listeningState.AllowedStateTransitions = new Map<StateName, State>([["busy", this.busyState], ["idle", this.idleState]]); this.busyState.AllowedStateTransitions = new Map<StateName, State>([["idle", this.idleState]]); this.currentState = this.idleState; this.currentState.onEnter(); } Now, the question arises that how do we detect when we need to transition from one state to another. For that we subscribe on the Snowboy Detector Observable. We are using Snowboy library for Hotword Detection. Snowboy detects whether an audio stream is silent, has some sound or whether hotword was spoken. We bind all this information to an observable using the ReactiveX Observable pattern. This gives us a stream of events to which we can subscribe and get the results. It can be understood in the following code snippet. detector.on("silence", () => { this.subject.next(DETECTOR.Silence); }); detector.on("sound", () => {}); detector.on("error", (error) => { console.error(error); }); detector.on("hotword", (index, hotword) => { this.subject.next(DETECTOR.Hotword); }); public get Observable(): Observable<DETECTOR> { return this.subject.asObservable(); } Now, in the idle state, we subscribe to the values emitted by the observable of the detector to know when a hotword…

Continue ReadingManaging States in SUSI MagicMirror Module