Implementing Sort By Date Feature In Susper

  Susper has been given ‘Sort By Date’ feature which provides the user with latest results with the latest date. This feature enhances the search experience and helps users to find desired results more accurately. The sorting of results date wise is done by yacy backend which uses Apache Solr technology. The idea was to create a ‘Sort By Date’ feature similar to the market leader. For example, if a user searches for keyword ‘Jaipur’ then results appear to be like this: If a user wishes to get latest results, they can use ‘Sort By Date’ feature provided under ‘Tools’. The above screenshot shows the sorted results. You may however notice that results are not arranged year wise. Currently, the backend work for this is being going on Yacy and soon will be implemented on the frontend as well once backend provide us this feature. Under ‘Tools’ we created an option for ‘Sort By Date’ simply using <li> tag. <ul class=”dropdown-menu”>   <li (click)=”filterByDate()”>Sort By Date</li> </ul> When clicked, it calls filterByDate() function to perform the following task: filterByDate() {   let urldata = Object.assign({}, this.searchdata);   urldata.query = urldata.query.replace(“/date”, “”);   this.store.dispatch(new queryactions.QueryServerAction(urldata)); } Earlier we were using ‘last_modified desc’ attribute provided by Solr for sorting out dates in descending order. In June 2017, this feature was deprecated with a new update of Solr. We are using /date attribute in query for sorting out results which is being provided by Solr. The source code for the implementation can be found here: https://github.com/fossasia/susper.com/blob/master/src/app/results/results.component.ts Resources: YaCy WebClient Bootstrap: https://github.com/yacy/yacy_webclient_bootstrap Solr Query Parameters documentation: https://cwiki.apache.org/confluence/display/solr/Common+Query+Parameters  

Continue ReadingImplementing Sort By Date Feature In Susper

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 Color Options in loklak Media Wall

Color options in loklak media wall gives user the ability to set colors for different elements of the media wall. Taking advantage of Angular two-way data binding property and ngrx/store, we can link up the CSS properties of the elements with concerned state properties which stores the user-selected color. This makes color customization fast and reactive for media walls. In this blog here, I am explaining the unidirectional workflow using ngrx for updating various colors and working of color customization. Flow Chart The flowchart below explains how the color as a string is taken as an input from the user and how actions, reducers and component observables link up to change the current CSS property of the font color. Working Designing Models: It is important at first to design model which must contain every CSS color property that can be customized. A single interface for a particular HTML element of media wall can be added so that color customization for a particular element can take at once with faster rendering. Here we have three interfaces: WallHeader WallBackground WallCard These three interfaces are the models for the three core components of the media wall that can be customized. export interface WallHeader { backgroundColor: string; fontColor: string; } export interface WallBackground { backgroundColor: string; } export interface WallCard { fontColor: string; backgroundColor: string; accentColor: string; }   Creating Actions: Next step is to design actions for customization. Here we need to pass the respective interface model as a payload with updated color properties. These actions when dispatched causes reducer to change the respective state property, and hence, the linked CSS color property. export class WallHeaderPropertiesChangeAction implements Action { type = ActionTypes.WALL_HEADER_PROPERTIES_CHANGE;constructor(public payload: WallHeader) { } } export class WallBackgroundPropertiesChangeAction implements Action { type = ActionTypes.WALL_BACKGROUND_PROPERTIES_CHANGE;constructor(public payload: WallBackground) { } } export class WallCardPropertiesChangeAction implements Action { type = ActionTypes.WALL_CARD_PROPERTIES_CHANGE;constructor(public payload: WallCard) { } }   Creating reducers: Now, we can proceed to create reducer functions so as to change the current state property. Moreover, we need to define an initial state which is the default state for uncustomized media wall. Actions can now be linked to update state property using this reducer when dispatched. These state properties serve two purposes: Updating Query params for Direct URL. Updating Media wall Colors case mediaWallCustomAction.ActionTypes.WALL_HEADER_PROPERTIES_CHANGE: { const wallHeader = action.payload;return Object.assign({}, state, { wallHeader }); }case mediaWallCustomAction.ActionTypes.WALL_BACKGROUND_PROPERTIES_CHANGE: { const wallBackground = action.payload;return Object.assign({}, state, { wallBackground }); }case mediaWallCustomAction.ActionTypes.WALL_CARD_PROPERTIES_CHANGE: { const wallCard = action.payload;return Object.assign({}, state, { wallCard }); }   Extracting Data to the component from the store: In ngrx, the central container for states is the store. Store is itself an observable and returns observable related to state properties. We have already defined various states for media wall color options and now we can use selectors to return state observables from the store. These observables can now easily be linked to the CSS of the elements which changes according to customization. private getDataFromStore(): void { this.wallCustomHeader$ = this.store.select(fromRoot.getMediaWallCustomHeader); this.wallCustomCard$ = this.store.select(fromRoot.getMediaWallCustomCard); this.wallCustomBackground$ =…

Continue ReadingAdding Color Options in loklak Media Wall

Auto-Refreshing Mode in loklak Media Wall

Auto-refreshing wall means that the request to the loklak server for the feeds must be sent after every few seconds and adding up new feeds in the media wall as soon as the response is received for a single session. For a nice implementation, it is also necessary to check if the new feeds are being received from the server and consequently, close the connection as soon as no feeds are received as to maintain session singularity. In this blog post, I am explaining how I implemented the auto-refreshing mode for media wall using tools like ngrx/store and ngrx/effects. Flow Chart The flowchart below explains the workflow of how the actions, effects and service are linked to create a cycle of events for auto-refreshing mode. It also shows up how the response is handled as a dependency for the next request. Since effects play a major role for this behaviour, we can say it as the “Game of Effects”. Working Effect wallSearchAction$: Assuming the Query for media wall has changed and ACTION: WALL_SEARCH has been dispatched, we will start from this point of time. Looking into the flowchart, we can see as soon the action WALL_SEARCH is dispatched, a effect needs to be created to detect the action dispatched.This effect customizes the query and sets up various configurations for search service and calls the service. Depending on whether the response is received or not, it either dispatches WallSearchCompleteSuccessAction or WallSearchCompleteFailAction respectively. Moreover, this effect is responsible for changing the route/location of the application. @Effect() wallSearchAction$: Observable<Action> = this.actions$ .ofType(wallAction.ActionTypes.WALL_SEARCH) .debounceTime(400) .map((action: wallAction.WallSearchAction) => action.payload) .switchMap(query => { const nextSearch$ = this.actions$.ofType(wallAction.ActionTypes.WALL_SEARCH).skip(1); const searchServiceConfig: SearchServiceConfig = new SearchServiceConfig();if (query.filter.image) { searchServiceConfig.addFilters(['image']); } else { searchServiceConfig.removeFilters(['image']); } if (query.filter.video) { searchServiceConfig.addFilters(['video']); } else { searchServiceConfig.removeFilters(['video']); }return this.apiSearchService.fetchQuery(query.queryString, searchServiceConfig) .takeUntil(nextSearch$) .map(response => { const URIquery = encodeURIComponent(query.queryString); this.location.go(`/wall?query=${URIquery}`); return new apiAction.WallSearchCompleteSuccessAction(response); }) .catch(() => of(new apiAction.WallSearchCompleteFailAction(''))); Property lastResponseLength: Looking into the flow chart, we can see that after WallSearchCompleteSuccessAction is dispatched, we need to check for the number of feeds in the response. If the number of feeds in the response is more than 0, we can continue to make a new request to the server. On the other hand, if no feeds are received, we need to close the connection and stop requesting for more feeds. This check is implemented using lastResponseLength state property of the reducer which maintains the length of the entities for the last response received. case apiAction.ActionTypes.WALL_SEARCH_COMPLETE_SUCCESS: { const apiResponse = action.payload;return Object.assign({}, state, { entities: apiResponse.statuses, lastResponseLength: apiResponse.statuses.length }); }   Effect nextWallSearchAction$: Now, we have all the information regarding if we should dispatch WALL_NEXT_PAGE_ACTION depending on the last response received. We need to implement an effect that detects WALL_SEARCH_COMPLETE_SUCCESS  keeping in mind that the next request should be made 10 seconds after the previous response is received. For this behaviour, we need to use debounceTime() which emits a value only after certain specified time period has passed. Here, debounce is set to 10000ms which is equal…

Continue ReadingAuto-Refreshing Mode in loklak Media Wall

Query Model Structure of Loklak Search

Need to restructure The earlier versions of loklak search applications had the issues of breaking changes whenever any new feature was added in the application. The main reason for these unintended bugs was identified to be the existing query structure. The query structure which was used in the earlier versions of the application only comprised of the single entity a string named as queryString. export interface Query { queryString: string; } This simple query string property was good enough for simple string based searches which were the goals of the application in the initial phases, but as the application progressed we realized this simple string based implementation is not going to be feasible for the long term. As there are only a limited things we can do with strings. It becomes extremely difficult to set and reset the portions of the string according to the requirements. This was the main vision for the alternate architecture design scheme was to the ease of enabling and disabling the features on the fly. Application Structure Therefore, to overcome the difficulties faced with the simple string based structure we introduced the concept of an attribute based structure for queries. The attribute based structure is simpler to understand and thus easier to maintain the state of the query in the application. export interface Query { displayString: string; queryString: string; routerString: string; filter: FilterList; location: string; timeBound: TimeBound; from: boolean; } The reason this is called an attribute based structure is that here each property of an interface is independent of one another. Thus each one can be thought of as a separate little key placed on the query, but each of these keys are different and are mutually exclusive. What this means is, if I want to write an attribute query, then it does not matter to me which other attributes are already present on the query. The query will eventually be processed and sent to the server and the corresponding valid results if exists will be shown to the user. Now the question arises how do we modify the values of these attributes? Now before answering this I would like to mention that this interface is actually instantiated in the the Redux state, so now our question automatically gets answered, the modification to redux state corresponding to the query structure will be done by specific reducers meant for modification of each attribute. These reducers are again triggered by corresponding actions. export const ActionTypes = { VALUE_CHANGE: '[Query] Value Change', FILTER_CHANGE: '[Query] Filter Change', LOCATION_CHANGE: '[Query] Location Change', TIME_BOUND_CHANGE: '[Query] Time Bound Change', }; This ActionTypes object contains the the corresponding actions which are used to trigger the reducers. These actions can be dispatched in response to any user interaction by any of the components, thus modifying a particular state attribute via the reducer. Converting from object to string Now for our API endpoint to understand our query we need to send the proper string in API accepted format. For this there is need to…

Continue ReadingQuery Model Structure of Loklak Search

Adding unit tests for effects in Loklak Search

Loklak search uses @ngrx/effects to listen to actions dispatched by the user and sending API request to the loklak server. Loklak search, currently, has seven effects such as Search Effects,  Suggest Effects which runs to make the application reactive. It is important to test these effects to ensure that effects make API calls at the right time and then map the response to send it back to the reducer. I will  explain here how I added unit tests for the effects. Surprisingly, the test coverage increased from 43% to 55% after adding these tests. Effects to test We are going to test effects for user search. This effect listens to the event of type USER_SEARCH and makes a call to the user-search service with the query as a parameter. After a response is received, it maps the response and passes it on the UserSearchCompleteSuccessAction action which performs the later operation. If the service fails to get a response, it makes a call to the UserSearchCompleteFailAction. Code ApiUserSearchEffects is the effect which detects if the USER_SEARCH action is dispatched from some component of the application and consequently, it makes a call to the UserSearchService and handles the JSON response received from the server. The effects then, dispatch the action new UserSearchCompleteSuccessAction if response is received from server or either dispatch the action new UserSearchCompleteFailAction if no response is received. The debounce time is set to 400 so that response can be flushed if a new USER_SEARCH is dispatched within the next 400ms. For this effect, we need to test if the effects actually runs when USER_SEARCH action is made. Further, we need to test if the correct parameters are supplied to the service and response is handled carefully. We also, need to check if the response if really flushed out within the certain debounce time limit. @Injectable() export class ApiUserSearchEffects {@Effect() search$: Observable<Action> = this.actions$ .ofType(userApiAction.ActionTypes.USER_SEARCH) .debounceTime(400) .map((action: userApiAction.UserSearchAction) => action.payload) .switchMap(query => { const nextSearch$ = this.actions$.ofType(userApiAction.ActionTypes.USER_SEARCH).skip(1);const follow_count = 10;return this.apiUserService.fetchQuery(query.screen_name, follow_count) .takeUntil(nextSearch$) .map(response => new userApiAction.UserSearchCompleteSuccessAction(response)) .catch(() => of(new userApiAction.UserSearchCompleteFailAction(''))); });constructor( private actions$: Actions, private apiUserService: UserService ) { } } Unit test for effects Configure the TestBed class before starting the unit test and add all the necessary imports (most important being the EffectsTestingModule) and providers. This step will help to isolate the effects completely from all other components and testing it independently. We also need to create spy object which spies on the method userService.fetchQuery with provider being UserService. beforeEach(() => TestBed.configureTestingModule({ imports: [ EffectsTestingModule, RouterTestingModule ], providers: [ ApiUserSearchEffects, { provide: UserService, useValue: jasmine.createSpyObj('userService', ['fetchQuery']) } ] })); Now, we will be needing a function setup which takes params which are the data to be returned by the Mock User Service. We can now configure the response to returned by the service. Moreover, this function will be initializing EffectsRunner and returning ApiUserSearchEffects so that it can be used for unit testing. function setup(params?: {userApiReturnValue: any}) { const userService = TestBed.get(UserService); if (params) { userService.fetchQuery.and.returnValue(params.userApiReturnValue); }return {…

Continue ReadingAdding unit tests for effects in Loklak Search

Adding Unit Test for Reducer in loklak search

Ngrx/store components are an integral part of the loklak search. All the components are dependent on how the data is received from the reducers. Reducer is like a client-side database which stores up all the data received from the API response. It is responsible for changing the state of the application. Reducers also supplies data to the Angular components from the central Store. If correct data is not received by the components, the application would crash. Therefore, we need to test if the reducer is storing the data in a correct way and changes the state of the application as expected. Reducer also stores the current state properties which are fetched from the APIs. We need to check if reducers store the data in a correct way and if the data is received from the reducer when called from the angular components. In this blog, I would explain how to build up different components for unit testing reducers. Reducer to test This reducer is used for store the data from suggest.json API from the loklak server.The data received from the server is further classified into three properties which can be used by the components to show up auto- suggestions related to current search query. metadata: - This property stores the metadata from API suggestion response. entities: - This property stores the array of suggestions corresponding to the particular query received from the server. valid: - This is a boolean which keeps a check if the suggestions are valid or not. We also have two actions corresponding to this reducer. These actions, when called, changes the state properties which , further, supplies data to the components in a more classified manner. Moreover, state properties also causes change in the UI of the component according to the action dispatched. SUGGEST_COMPLETE_SUCCESS: - This action is called when the data is received successfully from the server. SUGGEST_COMPLETE_FAIL: - This action is called when the retrieving data from the server fails. export interface State { metadata: SuggestMetadata; entities: SuggestResults[]; valid: boolean; }export const initialState: State = { metadata: null, entities: [], valid: true };export function reducer(state: State = initialState, action: suggestAction.Actions): State { switch (action.type) { case suggestAction.ActionTypes.SUGGEST_COMPLETE_SUCCESS: { const suggestResponse = action.payload;return { metadata: suggestResponse.suggest_metadata, entities: suggestResponse.queries, valid: true }; }case suggestAction.ActionTypes.SUGGEST_COMPLETE_FAIL: { return Object.assign({}, state, { valid: false }); }default: { return state; } } } Unit tests for reducers Import all the actions, reducers and mocks import * as fromSuggestionResponse from './suggest-response'; import * as suggestAction from '../actions/suggest'; import { SuggestResponse } from '../models/api-suggest'; import { MockSuggestResponse } from '../shared/mocks/suggestResponse.mock';   Next, we are going to test if the undefined action doesn’t a cause change in the state and returns the initial state properties. We will be creating an action by const action = {} as any;  and call the reducer by const result = fromSuggestionResponse.reducer(undefined, action);. Now we will be making assertions with expect() block to check if the result is equal to initialState and all the initial state properties are…

Continue ReadingAdding Unit Test for Reducer in loklak search