Dispatching a search action to get result for a query in Loklak Search

For a new comer in Loklak, it’s not really easy to understand the complete codebase to just get results for a query without explicitly using navigation route i.e. ‘/search’. In order to help newcomers to easily understand a simple way to get results for a query in the codebase, I am writing this blog post. Setting up a query A sample searchQuery will be created of type Query. And following discussion will provide a way to get the results for a sample query - ‘from:Fossasia’ (Last 30 days tweets from FOSSASIA). searchQuery: Query = { displayString: ‘from:FOSSASIA, queryString: ‘from:FOSSASIA’, routerString: ‘from:FOSSASIA’, filter: { video: false, image: false }, location: null, timeBound: { since: null, until: null }, from: true }   Process discussed here can be used to get results in any class file within Loklak Search. Using necessary imports First step would be to add all necessary imports. import { OnInit } from ‘@angular/core’; import { Store } from ‘@ngrx/store’; import { Observable } from ‘rxjs/Observable’; import * as fromRoot from ‘../../reducers’; import { Query } from ‘../../models/query’; import * as searchAction from ‘../../actions/api’; import { ApiResponseResult } from ‘../../models/api-response’;   Note: No need to import OnInit, if the class already implements it (OnInit is one of the Angular’s Lifecycle Hooks which controls the changes when the component containing it gets loaded initially). The Query is used to define the type of searchQuery variable created above. And ApiResponseResult is the type of result which will be obtained on searching the searchQuery. Declaring response variables and store object Next step would be to declare the variables with the type of response and create store object of type Store with the current State of the application. public isSearching$: Observable<boolean>; public apiResponseResults$: Observable<ApiResponseResult[]>; constructor( private store: Store<fromRoot.State> ) { }   isSearching$ holds an Observable which can be subscribed to get a boolean value which states the current status of searching of query. apiResponseResults$ holds the actual response of query. Dispatching a SearchAction The most crucial part comes here to dispatch a SearchAction with the created searchQuery as payload. A new method would be created with void as return type to dispatch the SearchAction. private queryFromURL(): void { this.store.dispatch( new searchAction.SearchAction(this.searchQuery) ); } Selecting and storing results from store A new method would be created to store the current searching status along with the response of input query in isSearching$ and apiResponseResults$ respectively. Now the selector for the reducer function corresponding to the search and api response entities would be called. private getDataFromStore(): void { this.isSearching$ = this.store.select(fromRoot.getSearchLoading); this.apiResponseResults$ = this.store.select( fromRoot.getApiResponseEntities ); } Dispatching and Selecting on view Init create ngOnInit() method, If you have not already done and call the respective dispatching and select method inside it respectively. ngOnInit() { this.queryFromURL(); this.getDataFromStore(); } How should one test the results? Call these lines inside any method and look for the results on browser console window. console.log(this.isSearching$); console.log(this.apiResponseResults$); Resources Loklak Search: Ngrx pattern Ngrx platform: Official Docs

Continue ReadingDispatching a search action to get result for a query in Loklak Search

Adding ‘Scroll to Top’ in Loklak Search

It is really important for a Progressive Web Application like Loklak to be user friendly. To enhance the user experience and help user scroll up through the long list of results, a new ‘Scroll to Top’ feature has been added to Loklak. Integration Process The feature is built using HostListener and Inject property of Angular, which allows to use Window and Document functionalities similar to Javascript. Adding HostListener First step would be to import HostListener and Inject, and create property of document using with Inject. import { HostListener, Inject } from ‘@angular/core’; navIsFixed: boolean; constructor( @Inject(DOCUMENT) private document: Document ) { }   Next step would be to create method to detect the current state of window, and scroll to the top of window. @HostListener(‘window:scroll’, []) onWindowScroll() { if (window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop > 100) { this.navIsFixed = true; } else if (this.navIsFixed && window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop < 10) { this.navIsFixed = false; } } scrollToTop() { (function smoothscroll() { const currentScroll = document.documentElement.scrollTop || document.body.scrollTop; if (currentScroll > 0) { window.requestAnimationFrame(smoothscroll); window.scrollTo(0, currentScroll – (currentScroll / 5)); } })(); }   The last step would be to add user interface part i.e. HTML and CSS component for the Scroll to Top feature. Creating user interface HTML HTML portion contains a div tag with class set to CSS created and a button with click event attached to it to call the scrollToTop() method created above. CSS The CSS class corresponding to the div tag provides the layout of the button. button { display: block; margin: 32px auto; font-size: 1.1em; padding: 5px; font-weight: 700; background: transparent; border: none; cursor: pointer; height: 40px; width: 40px; font-size: 30px; color: white; background-color: #d23e42; border-radius: 76px; &:focus { border: none; outline: none; } } .scroll-to-top { position: fixed; bottom: 15px; right: 15px; opacity: 0; transition: all .2s ease-in-out; } .show-scroll { opacity: 1; transition: all .2s ease-in-out; }   The button style creates a round button with an interactive css properties. scroll-to-top class defines the behaviour of the div tag when user scrolls on the results page. How to use? Goto Loklak and search a query. On results page, scroll down the window to see upward arrow contained in a red circle like: On clicking the button, user should be moved to the top of results page. Resources W3C: How TO - Scroll Back To Top Button Angular docs. Angular.io: HostListener

Continue ReadingAdding ‘Scroll to Top’ in Loklak Search

Adding Speech Component in Loklak Search

Speech recognition service for voice search is already embedded in Loklak. Now the idea is to use this service and create a new separate component for voice recognition with an interactive and user friendly interface. This blog will cover every single portion of an Angular’s redux based component from writing actions and reducers to the use of created component in other required components. Creating Action and Reducer Function The main idea to create an Action is to control the flow of use of Speech Component in Loklak Search. The Speech Component will be called on and off based on this Action. Here, the first step is to create speech.ts file in actions folder with the following code: import { Action } from '@ngrx/store'; export const ActionTypes = {    MODE_CHANGE: '[Speech] Change', }; export class SearchAction implements Action {    type = ActionTypes.MODE_CHANGE;    constructor(public payload: any) {} } export type Actions    = SearchAction;   In the above segment, only one action (MODE_CHANGE) has been created which is like a boolean value which is being returned as true or false i.e. whether the speech component is currently in use or not. This is a basic format to be followed in creating an Action which is being followed in Loklak Search. The next step would be to create speech.ts file in reducers folder with the following code: import { Action } from '@ngrx/store'; import * as speech from '../actions/speech'; export const MODE_CHANGE = 'MODE_CHANGE'; export interface State { speechStatus: boolean; } export const initialState: State = { speechStatus: false }; export function reducer(state: State = initialState, action: speech.Actions): State { switch (action.type) { case speech.ActionTypes.MODE_CHANGE: { const response = action.payload; return Object.assign({}, state, {speechStatus: response}); } default: { return state; } } } export const getspeechStatus = (state: State) => state.speechStatus;   It follows the format of reducer functions created in Loklak Search. Here, the main key point is the state creation and type of value it is storing i.e. State is containing a speechStatus of type boolean. Defining an initial state with speechStatus value false (Considering initially the Speech Component will not be in use). The reducer function a new state by toggling the input state based on the type of Action created above and it returns the input state by default. At last wrapping the state as a function and returning the state’s speechStatus value. Third and last step in this section would be to create a selector for the above reducer function in the root reducer index file. Import and add speech from speech reducer file into the general state in root reducer file. And at last export the created selector function for speech reducer. import * as fromSpeech from './speech'; export interface State { ... speech: fromSpeech.State; } export const getSpeechState = (state: State) => state.speech; export const getspeechStatus = createSelector( getSpeechState, fromSpeech.getspeechStatus); Creating Speech Component Now comes the main part to create and define the functioning of Speech Component. For creating the basic Speech Component, following command is used:…

Continue ReadingAdding Speech Component in Loklak Search

Adding new test cases for increasing test coverage of Loklak Server

It is a good practice to have test cases covering major portion of actual code base. The idea was same to add new test cases in Loklak Server to increase its test coverage. The results were quite amazing with a significant increase of about 3% in total test coverage of the overall project. And about 80-100% increase in the test coverage of individual files for which tests have been written. Integration Process For integration, a total of 6 new test cases have been written: ASCIITest GeoLocationTest CommonPatternTest InstagramProfileScraperTest EventBriteCrawlerServiceTest LocationWiseTimeServiceTest For increasing code coverage, Java docs have been written which increase the lines of code being covered. Implementation Basic implementation of adding a new test case is same. Here’s is an example of EventBriteCrawlerServiceTest implementation. This can be used as a reference for adding a new test case in the project. Prerequisite: If the test file being written tests any additional external service (e.g. EventBriteCrawlerServiceTest tests any event being created on EventBrite platform) then, a corresponding new test service or test event should be written beforehand on the given platform. For EventBriteCrawlerServiceTest, a test-event has been created. The given below steps will be followed for creating a new test case (EventBriteCrawlerServiceTest), assuming the prerequisite step has been done: A new java file is generated in test/org/loklak/api/search/ as EventBriteCrawlerServiceTest.java. Define package for the test file and import EventBriteCrawlerService.java file which has to be tested along with necessary packages and methods. package org.loklak.api.search; import org.loklak.api.search.EventBriteCrawlerService; import org.loklak.susi.SusiThought; import org.junit.Test; import org.json.JSONArray; import org.json.JSONObject; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertNotNull;   Define the class and methods that need to be tested. It is a good idea to break the testing into several test methods rather than testing more features in a single method. public class EventBriteCrawlerServiceTest {   @Test   public void apiPathTest() { }   @Test   public void eventBriteEventTest() { }   @Test   public void eventBriteOrganizerTest() { }   @Test   public void eventExtraDetailsTest() { } }   Create an object of EventBriteCrawlerService in each test method. EventBriteCrawlerService eventBriteCrawlerService = new EventBriteCrawlerService();   Call specific method of EventBriteCrawlerService that needs to be tested in each of the defined methods of test file and assert the actualresult to the expectedresult. @Test public void apiPathTest() {     EventBriteCrawlerService eventBriteCrawlerService = new EventBriteCrawlerService();      assertEquals("/api/eventbritecrawler.json", eventBriteCrawlerService.getAPIPath()); }   For methods fetching an actual result (Integration tests) of event page, define and initialise an expected set of results and assert the expected result with the actual result by parsing the json result. @Test public void eventBriteOrganizerTest() { EventBriteCrawlerService eventBriteCrawlerService = new EventBriteCrawlerService(); String eventTestUrl = "https://www.eventbrite.com/e/ testevent-tickets-46017636991"; SusiThought resultPage = eventBriteCrawlerService .crawlEventBrite(eventTestUrl); JSONArray jsonArray = resultPage.getData(); JSONObject organizer_details = jsonArray.getJSONObject(1); String organizer_contact_info = organizer_details .getString("organizer_contact_info"); String organizer_link = organizer_details .getString("organizer_link"); String organizer_profile_link = organizer_details .getString("organizer_profile_link"); String organizer_name = organizer_details .getString("organizer_name"); assertEquals("https://www.eventbrite.com /e/testevent-tickets-46017636991 #lightbox_contact", organizer_contact_info); assertEquals("https://www.eventbrite.com /e/testevent-tickets-46017636991 #listing-organizer", organizer_link); assertEquals("", organizer_profile_link); assertEquals("aurabh Srivastava", organizer_name); }   If the test file is testing the harvester, then import and add the test class in TestRunner.java file. e.g. import org.loklak.harvester.TwitterScraperTest; @RunWith(Suite.class) @Suite.SuiteClasses({   TwitterScraperTest.class }) Testing…

Continue ReadingAdding new test cases for increasing test coverage of Loklak Server

Adding ‘Voice Search’ Feature in Loklak Search

It is beneficial to have a voice search feature embedded in a search engine like loklak.org based on PWA (Progressive Web Application) architecture. This will allow users to make query using voice on phone or computer. For integrating voice search, JavaScript Web Speech API (also known as webkitSpeechRecognition) is used which allows us to add speech recognition feature in any website or a web application. Integration Process For using webkitSpeechRecognition, a separate typescript service is being created along with its relevant unit test speech.service.ts speech.service.spec.ts The main idea to implement the voice search was To create an injectable speech service. Write methods for starting and stopping voice record in it. Import and inject the created speech service into root app module. Use the created speech service into required HomeComponent and FeedHeaderComponent. Structure of Speech Service The speech service mainly consists of two methods record() method which accepts lang as string input which specifies the language code for speech recording and returns a string Observable.    record(lang: string): Observable<string> {            return Observable.create(observe => {                  const webkitSpeechRecognition }: IWindow = <IWindow>window;                  this.recognition = new webkitSpeechRecognition();                  this.recognition.continuous = true;                  this.recognition.interimResults = true;                  this.recognition.onresult = take => this.zone.run(()                             observe.next(take .results.item (take.results. length - 1). item(0). transcript);                  this.recognition .onerror = err => observe .error(err);                  this.recognition.onend = () => observe .complete();                  this.recognition.lang = lang;                  this.recognition.start( );         });      }   Using observe.complete() allows speech recognition action to stop when the user stops speaking. stoprecord() method which is used to stop the current instance of recording.  stoprecord() {        if (this.recognition) {            this.recognition.stop();        }    }   stop() method is used to stop the current instance of speech recognition. Using Speech Service in required Component In Loklak Search, the speech service have been included in two main components i.e. HomeComponent and FeedHeaderComponent. The basic idea of using the created speech service in these components is same. In the TypeScript (not spec.ts) file of the two components, firstly import the SpeechService and create its object in constructor. constructor( private speech: SpeechService ) { }   Secondly, define a speechRecognition() method and use the created instance or object of SpeechService in it to record speech and set the query as the recorded speech input. Here, default language has been set up as ‘en_US’ i.e. English.  stoprecord() {        if (this.recognition) {            this.recognition.stop();        }    }   After user clicks on speech icon, this method will be called and the recorded speech will be set as the query and there will be router navigation to the /search page where the result will be displayed. Resources George Ornbo (2014). Shapeshed: The HTML5 Speech Recognition API Glen Shires, Hans Wennborg (2012). W3C: Web Speech API Specification

Continue ReadingAdding ‘Voice Search’ Feature in Loklak Search