Adding different metrics sections to the start page

In the initial version of the SUSI.AI Skill CMS we simply displayed all the skills present in the system in the form of cards. Once the skill analytics was incorporated into the CMS we got a bunch of skill statistics and thus we enhanced the start page by incorporating horizontally scrollable skill cards as per skill metrics like top rated skills, most used skills, skills which have received the most feedback etc. I worked on adding the skills with most feedback section and the section for the top games. This post will majorly deal with how the metrics sections are implemented on the start page and how any new metrics can be incorporated into the system and thus displayed on the CMS. About the API /cms/getSkillMetricsData.json?language=${language} Sample API call: https://api.susi.ai/cms/getSkillMetricsData.json?language=en   This will return a JSON which contains the skill data for all the metrics. { "accepted": true, "model": "general", "group": "All", "language": "en", "metrics": { "newest": [...], "rating": [...], ... } "message": "Success: Fetched skill data based on metrics", "session": {"identity": { "type": "host", "name": "162.158.23.7_68cefd16", "anonymous": true }} }   All of the data for several metics comes from the metrics object of the response which in turn contains arrays of skill data for each metric. CMS Implementation Once the BrowseSkill component is mounted we make an API call to the server to fetch all the data and save it to the component state, this data is then fed to the ScrollCardList component as props and the scroll component is rendered with appropriate data for different metrics. loadMetricsSkills = () => { let url; url = urls.API_URL + '/cms/getSkillMetricsData.json?language=' + this.state.languageValue; let self = this; $.ajax({ url: url, dataType: 'jsonp', jsonp: 'callback', crossDomain: true, success: function(data) { self.setState({ skillsLoaded: true, staffPicksSkills: data.metrics.staffPicks, topRatedSkills: data.metrics.rating, topUsedSkills: data.metrics.usage, latestUpdatedSkills: data.metrics.latest, newestSkills: data.metrics.newest, topFeedbackSkills: data.metrics.feedback, topGames: data.metrics['Games, Trivia and Accessories'], }); }, error: function(e) { console.log('Error while fetching skills based on top metrics', e); return self.loadMetricsSkills(); }, }); };   We are using a single component for skill metrics and skill listing which show up on applying any filter or visiting any category. Thus we think of a condition when the skill metrics are to be displayed and conditionally render the metrics section depending on the condition. So the metrics section shows up only when we have not visited any category or language page, there’s no search query in the search bar, there’s no rating refine filter applied and no time filter applied. let metricsHidden = this.props.routeType || this.state.searchQuery.length > 0 || this.state.ratingRefine || this.state.timeFilter;   Depending on the section you want to display, pass appropriate data as props to the SkillCardScrollList component, say we want to display the section with most feedback {this.state.topFeedbackSkills.length && !metricsHidden ? ( <div style={metricsContainerStyle}> <div style={styles.metricsHeader} className="metrics-header" > <h4> {'"SUSI, what are the skills with most feedback?"'} </h4> </div> {/* Scroll Id must be unique for all instances of SkillCardList*/} {!this.props.routeType && ( <SkillCardScrollList scrollId="topFeedback" skills={this.state.topFeedbackSkills} modelValue={this.state.modelValue} languageValue={this.state.languageValue} skillUrl={this.state.skillUrl} /> )} </div> ) : null}  …

Continue ReadingAdding different metrics sections to the start page

Implementing the Order Receipt End Point in Orga App

In the  Open Event Orga App, I have implemented the Order Receipt endpoint with the help of which the organizer will be able to send the receipt of the ‘completed’ orders to the attendee via email. Initially the API was made in the server and then it was implemented in the the app. Following steps were followed: Firstly a method named sendReceipt was made in the OrderDetailFragment as follows. We pass in the orderIdentifier string as a parameter. private void sendReceipt() {   orderDetailViewModel.sendReceipt(orderIdentifier); } Now we implement 2 classes for OrderReceiptRequest and OrderReceiptResponse. The implementation is as follows. In the OrderReceiptRequest class we add just the orderIdentifier instance variable as the request involves just the order identifier parameter. @Data @JsonNaming(PropertyNamingStrategy.KebabCaseStrategy.class) public class OrderReceiptRequest {    public String orderIdentifier; } Now we implement the OrderReceiptResponse class which will consist of 2 parameters message and error. public class OrderReceiptResponse {   public String message;   public String error; } In the OrderDetailsViewModel we add the following method. We create an object OrderReceipt where we pass the orderIdentifier. In the following statements we call the sendReceipts method of OrderRepositorry which takes in this OrderReceiptRequest as parameter. public void sendReceipt(String orderIdentifier) {   OrderReceiptRequest orderReceipt = new OrderReceiptRequest();   orderReceipt.setOrderIdentifier(orderIdentifier);   compositeDisposable.add(orderRepository.sendReceipt(orderReceipt)       .doOnSubscribe(disposable -> progress.setValue(true))       .doFinally(() -> progress.setValue(false))       .subscribe(() -> success.setValue("Email Sent!"),           throwable -> error.setValue(ErrorUtils.getMessage(throwable).toString()))); } We then add the method sendReceipt in Order Repository which returns a Completable. Now we implement the sendReceipt methid in OrderRepositoryImpl as follows. First we check whether the repository is connected or not. If not then a network error message is sent.Then the sendReceiptEmail method present in the Orderapi class is called where we pass the orderReceiptRequest object. The next step will show the adding of the API for this particular end point. @Override public Completable sendReceipt(OrderReceiptRequest orderReceiptRequest) {   if (!repository.isConnected())       return Completable.error(new Throwable(Constants.NO_NETWORK));   return orderApi           .sendReceiptEmail(orderReceiptRequest)           .flatMapCompletable(               var -> Completable.complete())           .subscribeOn(Schedulers.io())           .observeOn(AndroidSchedulers.mainThread()); } Now in the OrdersAPI interface the following API call is written. We pass the OrderReceiptRequest in the body and the respinse is collected in the OrderReceiptRequest class and diplayed as the outcome. @POST("attendees/send-receipt") Observable<OrderReceiptResponse> sendReceiptEmail(@Body OrderReceiptRequest orderReceiptRequest); Certain UI changes also had to be done which are shown below. <LinearLayout   android:id="@+id/emailReceiptLl"   android:layout_width="0dp"   android:layout_height="wrap_content"   android:layout_weight="1"   android:layout_gravity="center"   android:gravity="center"   android:orientation="vertical">   <Button       android:id="@+id/emailReceipt"       android:layout_width="30dp"       android:layout_height="30dp"       android:background="@drawable/ic_email"       app:backgroundTint="@color/materialcolorpicker__white" />   <TextView       android:layout_width="wrap_content"       android:layout_height="wrap_content"       android:text="@string/receipt"       android:textAllCaps="true"       android:textSize="10sp"       android:textColor="@color/materialcolorpicker__white"/> </LinearLayout>  Resources Using Observables and Completables https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0 Medium article on RxJava https://blog.aritraroy.in/the-missing-rxjava-2-guide-to-supercharge-your-android-development-part-1-624ef326bff4

Continue ReadingImplementing the Order Receipt End Point in Orga App

Adding Push endpoint to send data from Loklak Search to Loklak Server

To provide enriched and sufficient amount of data to Loklak, Loklak Server should have multiple sources of data. The api/push.json endpoint of loklak server is used in Loklak to post the search result object to server. It will increase the amount and quality of data on server once the Twitter api is supported by Loklak (Work is in progress to add support for twitter api in loklak). Creating Push Service The idea is to create a separate service for making a Post request to server. First step would be to create a new ‘PushService’ under ‘services/’ using: ng g service services/push Creating model for Push Api Response Before starting to write code for push service, create a new model for the type of response data obtained from Post request to ‘api/push.json’. For this, create a new file push.ts under ‘models/’ with the code given below and export the respective push interface method in index file. export interface PushApiResponse { status: string; records: number; mps: number; message: string; } Writing Post request in Push Service Next step would be to create a Post request to api/push.json using HttpClient module. Import necessary dependencies and create an object of HttpClient module in constructor and write a PostData() method which would take the data to be send, makes a Post request and returns the Observable of PushApiResponse created above. import { Injectable } from ‘@angular/core’; import { HttpClient, HttpHeaders, HttpParams } from ‘@angular/common/http’; import { Observable } from ‘rxjs’; import { ApiResponse, PushApiResponse } from ‘../models’; @Injectable({ providedIn: ‘root’ }) export class PushService { constructor( private http: HttpClient ) { } public postData(data: ApiResponse): Observable<PushApiResponse> { const httpUrl = ‘https://api.loklak.org/ api/push.json’; const headers = new HttpHeaders({ ‘Content-Type’: ‘application/ x-www-form-urlencoded’, ‘Accept’: ‘application/json’, ‘cache-control’: ‘no-cache’ }); const {search_metadata, statuses} = data; // Converting the object to JSON string. const dataToSend = JSON.stringify({ search_metadata: search_metadata, statuses}); // Setting the data to send in // HttpParams() with key as ‘data’ const body = new HttpParams() .set(‘data’, dataToSend); // Making a Post request to api/push.json // endpoint. Response Object is converted // to PushApiResponse type. return this.http.post<PushApiResponse>( httpUrl, body, {headers: headers }); } }   Note: Data (dataToSend) send to backend should be exactly in same format as obtained from server. Pushing data into service dynamically Now the main part is to provide the data to be send into the service. To make it dynamic, import the Push Service in ‘api-search.effects.ts’ file under effects and create the object of Push Service in its constructor. import { PushService } from ‘../services’; constructor( … private pushService: PushService ) { }   Now, call the pushService object inside ‘relocateAfterSearchSuccess$’ effect method and pass the search response data (payload value of search success action) inside Push Service’s postData() method. @Effect() relocateAfterSearchSuccess$: Observable<Action> = this.actions$ .pipe( ofType( apiAction.ActionTypes .SEARCH_COMPLETE_SUCCESS, apiAction.ActionTypes .SEARCH_COMPLETE_FAIL ), withLatestFrom(this.store$), map(([action, state]) => { this.pushService .postData(action[‘payload’]); … ); Testing Successful Push to Backend To test the success of Post request, subscribe to the response data and print the response data on…

Continue ReadingAdding Push endpoint to send data from Loklak Search to Loklak Server

Open Event Server – Pages API

This article illustrates how the Pages API has been designed and implemented on the server side, i.e., FOSSASIA‘s Open Event Server. Pages endpoint is used to create static pages such as “About Page” or any other page that doesn’t need to be updated frequently and only a specific content is to be shown. Parameters name - This stores the name of the page. Type - String Required - Yes title - This stores the title of the page. Type - String Required - No url - This stores the url of the page. Type - String Required - Yes description - This stores the description of the page. Type - String Required - Yes language - This stores the language of the page. Type - String Required - No index - This stores the position of the page. Type - Integer Required - No Default - 0 place - Location where the page will be placed. Type - String Required - No Accepted Values - ‘footer’ and ‘event’ These are the allowed parameters for the endpoint. Model Lets see how we model this API. The ORM looks like this : __tablename__ = 'pages' id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String, nullable=False) title = db.Column(db.String) url = db.Column(db.String, nullable=False) description = db.Column(db.String) place = db.Column(db.String) language = db.Column(db.String) index = db.Column(db.Integer, default=0) As you can see, we created a table called “pages”. This table has 8 columns, 7 of which are the parameters that I have mentioned above. The column “id” is an Integer column and is the primary key column. This will help to differentiate between the various entries in the table. The visualisation for this table looks as follows : API We support the following operations: GET all the pages in the database POST create a new page GET details of a single page as per id PATCH a single page by id DELETE a single page by id To implement this we first add the routes in our python file as follows : api.route(PageList, 'page_list', '/pages') api.route(PageDetail, 'page_detail', '/pages/<int:id>') Then we define these classes to handle the requests. The first route looks as follows: class PageList(ResourceList):   """   List and create page   """   decorators = (api.has_permission('is_admin', methods="POST"),)   schema = PageSchema   data_layer = {'session': db.session,                 'model': Page} As can be seen above, this request requires the user to be an admin. It uses the Page model described above and handles a POST request. The second route is: class PageDetail(ResourceDetail):   """   Page detail by id   """   schema = PageSchema   decorators = (api.has_permission('is_admin', methods="PATCH,DELETE"),)   data_layer = {'session': db.session,                 'model': Page} This route also requires the user to be an admin. It uses the Page model and handles PATCH, DELETE requests. To summarise our APIs are: GET /v1/pages{?sort,filter} POST /v1/pages{?sort,filter} GET /v1/pages/{page_id} PATCH /v1/pages/{page_id} DELETE /v1/pages/{page_id} References Flask-Marshmallow SQLAlchemy Open Event Server Flask

Continue ReadingOpen Event Server – Pages API