Using Universal Image Loader to Display Image on Phimpme Android Application

In Phimpme Android application we needed to load image on the sharing Activity fast so that there won’t be any delay that is visible by a user in the loading of any activity. We used Universal Image Loader to load the image on the sharing Activity to load Image faster. Getting Universal Image Loader To get Universal Image Loader in your application go to Gradle(app)-> and then add the following line of code inside dependencies: dependencies{ compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.4' } Initialising Universal Image Loader and Displaying Image To display image on using Universal Image Loader we need to convert the image into a URI from a file path: saveFilePath = getIntent().getStringExtra(EXTRA_OUTPUT); Uri uri = Uri.fromFile(new File(saveFilePath)); How an image should be displayed We need to display the image in such a way that it covers the whole image view in the sharing Activity. The image should be zoomed out. The quality of the image should not be distorted or reduced. The image should look as it is. The image should be zoomable so that the user can pinch to zoom in and zoom out. For the image to adjust the whole Image View we set ImageScaleType.EXACTLY_STRETCHED. We will also set cacheInMemory to true and cacheOnDisc to true.   private void initView() {   saveFilePath = getIntent().getStringExtra(EXTRA_OUTPUT);   Uri uri = Uri.fromFile(new File(saveFilePath));   ImageLoader imageLoader = ((MyApplication)getApplicationContext()).getImageLoader();   DisplayImageOptions options = new DisplayImageOptions.Builder()           .cacheOnDisc(true)           .imageScaleType(ImageScaleType.EXACTLY_STRETCHED)           .cacheInMemory(true)           .bitmapConfig(Bitmap.Config.RGB_565)           .build();   imageLoader.displayImage(uri.toString(), shareImage, options); } Image Loader function in MyApplication class: private void initImageLoader() {   File cacheDir = com.nostra13.universalimageloader.utils.StorageUtils.getCacheDirectory(this);   int MAXMEMONRY = (int) (Runtime.getRuntime().maxMemory());   // System.out.println("dsa-->"+MAXMEMONRY+"   "+(MAXMEMONRY/5));//.memoryCache(new   // LruMemoryCache(50 * 1024 * 1024))   DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()           .cacheInMemory(true)           .cacheOnDisk(true)           .build();   ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(           this).memoryCacheExtraOptions(480, 800).defaultDisplayImageOptions(defaultOptions)           .diskCacheExtraOptions(480, 800, null).threadPoolSize(3)           .threadPriority(Thread.NORM_PRIORITY - 2)           .tasksProcessingOrder(QueueProcessingType.FIFO)           .denyCacheImageMultipleSizesInMemory()           .memoryCache(new LruMemoryCache(MAXMEMONRY / 5))           .diskCache(new UnlimitedDiskCache(cacheDir))           .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default           .imageDownloader(new BaseImageDownloader(this)) // default           .imageDecoder(new BaseImageDecoder(false)) // default           .defaultDisplayImageOptions(DisplayImageOptions.createSimple()).build();   this.imageLoader = ImageLoader.getInstance();   imageLoader.init(config); } Image View in Sharing Activity XML file: In the Sharing Activity Xml resource, we need to specify the width of the image view and the height of the image view. In Phimpme Android application we are using ImageViewTouch so that we have features like touch to zoom in zoom out. The scale type of the imageView is centerCrop so that image which is loaded is zoomed out and focus is in the center of the image.   <org.fossasia.phimpme.editor.view.imagezoom.ImageViewTouch   android:layout_width="match_parent"   android:layout_height="wrap_content"   android:id="@+id/share_image"   android:layout_below="@+id/toolbar"   android:layout_weight="10"   android:layout_alignParentStart="true"   android:scaleType="centerCrop"/> Conclusion To load image faster on any ImageView we should use Universal Image Loader. It helps load the activity faster and allows many features as discussed in the blog.   Github https://github.com/fossasia/phimpme-android Resources Github Link for Universal Image Loader: https://github.com/nostra13/Android-Universal-Image-Loader Documentation: https://github.com/nostra13/Android-Universal-Image-Loader/wiki

Continue ReadingUsing Universal Image Loader to Display Image on Phimpme Android Application

Auto Updating SUSI Android APK and App Preview on appetize.io

This blog will cover the way in which the SUSI Android APK is build automatically after each commit and pushed to “apk” branch in the github repo. Other thing which will be covered is that how the app preview on appetize.io can be updated after each commit. This is basically for the testers who wish to test the SUSI Android App. There are four ways to test the SUSI Android App. One is to simply download the alpha version of the app from the Google PlayStore. Here is the link to the app. Join the alpha testing and report bugs on the github issue tracker of the repo. Other way is to build the app from Android Studio but you may need to set the complete project. If you are looking to contribute in the project, this is the advised way to test the app. The other two ways are explained below. Auto Building of APK and pushing to “apk” branch We have written a script which does following steps whenever a PR is merged: Checks if the commit is of a PR or a commit to repo If not of PR, configures a user whose github account will be used to push the APKs. Clones the repo, generates the debug and release APK. Deletes everything in the apk branch. Commits and Pushes new changes to apk branch. This script is written for people or testers who do not have android studio installed in their computer and want to test the app. So, they can directly download the apk from the apk branch and install it in their phone. The APK is always updated after each commit. So, whenever a tester downloads the APK from apk branch, he will always get the latest app. if [[ $CIRCLE_BRANCH != pull* ]] then git config --global user.name "USERNAME" git config --global user.email "EMAIL" git clone --quiet --branch=apk https://USERNAME:$GITHUB_API_KEY@github.com/fossasia/susi_android apk > /dev/null ls cp -r ${HOME}/${CIRCLE_PROJECT_REPONAME}/app/build/outputs/apk/app-debug.apk apk/susi-debug.apk cp -r ${HOME}/${CIRCLE_PROJECT_REPONAME}/app/build/outputs/apk/app-release-unsigned.apk apk/susi-release.apk cd apk git checkout --orphan workaround git add -A git commit -am "[Circle CI] Update Susi Apk" git branch -D apk git branch -m apk git push origin apk --force --quiet > /dev/null fi Auto Updating of App Preview on appetize.io The APKs generated in the above step can now be used to set up the preview of the app on the appetize.io. Appetize.io is an online simulator to run mobile apps ( IOS and Android). Appetize.io provides a nice virtual mobile frame to run native apps with various options like screen size, mobile, OS version, etc. Appetize.io provides some API to update/publish the app. In SUSI, we once uploaded the app on appetize.io and now we are using the API provided by them to update the APK everytime a commit is pushed in the repository. API information (Derived from official docs of appetize.io): You may upload a new version of an existing app, or update app settings. Send an HTTP POST request to https://APITOKEN@api.appetize.io/v1/apps/PUBLICKEY Replace APITOKEN with your API token and…

Continue ReadingAuto Updating SUSI Android APK and App Preview on appetize.io

Adding Tweet Streaming Feature in World Mood Tracker loklak App

The World Mood Tracker was added to loklak apps with the feature to display aggregated data from the emotion classifier of loklak server. The next step in the app was adding the feature to display the stream of Tweets from a country as they are discovered by loklak. With the addition of stream servlet in loklak, it was possible to utilise it in this app. In this blog post, I will be discussing the steps taken while adding to introduce this feature in World Mood Tracker app. Props for WorldMap component The WorldMap component holds the view for the map displayed in the app. This is where API calls to classifier endpoint are made and results are displayed on the map. In order to display tweets on clicking a country, we need to define react props so that methods from higher level components can be called. In order to enable props, we need to change the constructor for the component - export default class WorldMap extends React.Component { constructor(props) { super(props); ... } ... } [SOURCE] We can now pass the method from parent component to enable streaming and other components can close the stream by using props in them - export default class WorldMoodTracker extends React.Component { ... showStream(countryName, countryCode) { /* Do something to enable streaming component */ ... } render() { return ( ... <WorldMap showStream={this.showStream}/> ... ) } } [SOURCE] Defining Actions on Clicking Country Map As mentioned in an earlier blog post, World Mood Tracker uses Datamaps to visualize data on a map. In order to trigger a piece of code on clicking a country, we can use the "done" method of the Datamaps instance. This is where we use the props passed earlier - done: function(datamap) { datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) { props.showStream(geography.properties.name, reverseCountryCode(geography.id)); }) } [SOURCE] The name and ID for the country will be used to display name and make API call to stream endpoint respectively. The StreamOverlay Component The StreamOverlay components hold all the utilities to display the stream of Tweets from loklak. This component is used from its parent components whose state holds info about displaying this component - export default class WorldMoodTracker extends React.Component { ... getStreamOverlay() { if (this.state.enabled) { return (<StreamOverlay show={true} channel={this.state.channel} country={this.state.country} onClose={this.onOverlayClose}/>); } } render() { return ( ... {this.getStreamOverlay()} ... ) } } [SOURCE] The corresponding props passed are used to render the component and connect to the stream from loklak server. Creating Overlay Modal On clicking the map, an overlay is shown. To display this overlay, react-overlays is used. The Modal component offered by the packages provides a very simple interface to define the design and interface of the component, including style, onclose hook, etc. import {Modal} from 'react-overlays'; <Modal aria-labelledby='modal-label' style={modalStyle} backdropStyle={backdropStyle} show={true} onHide={this.close}> <div style={dialogStyle()}> ... </div> </Modal> [SOURCE] It must be noted that modalStyle and backdropStyle are React style objects. Dialog Style The dialog style is defined to provide some space at the top, clicking where, the overlay is closed.…

Continue ReadingAdding Tweet Streaming Feature in World Mood Tracker loklak App

Using Protractor for UI Tests in Angular JS for Loklak Apps Site

Loklak apps site’s home page and app details page have sections where data is dynamically loaded from external javascript and json files. Data is fetched from json files using angular js, processed and then rendered to the corresponding views by controllers. Any erroneous modification to the controller functions might cause discrepancies in the frontend. Since Loklak apps is a frontend project, any bug in the home page or details page will lead to poor UI/UX. How do we deal with this? One way is to write unit tests for the various controller functions and check their behaviours. Now how do we test the behaviours of the site. Most of the controller functions render something on the view. One thing we can do is simulate the various browser actions and test site against known, accepted behaviours with Protractor. What is Protractor Protractor is end to end test framework for Angular and AngularJS apps. It runs tests against our app running in browser as if a real user is interacting with our browser. It uses browser specific drivers to interact with our web application as any user would. Using Protractor to write tests for Loklak apps site First we need to install Protractor and its dependencies. Let us begin by creating an empty json file in the project directory using the following command. echo {} > package.json Next we will have to install Protractor. The above command installs protractor and webdriver-manager. After this we need to get the necessary binaries to set up our selenium server. This can be done using the following. ./node_modules/protractor/bin/webdriver-manager update ./node_modules/protractor/bin/webdriver-manager start Let us tidy up things a bit. We will include these commands in package.json file under scripts section so that we can shorten our commands. Given below is the current state of package.json { "scripts": { "start": "./node_modules/http-server/bin/http-server", "update-driver": "./node_modules/protractor/bin/webdriver-manager update", "start-driver": "./node_modules/protractor/bin/webdriver-manager start", "test": "./node_modules/protractor/bin/protractor conf.js" }, "dependencies": { "http-server": "^0.10.0", "protractor": "^5.1.2" } } The package.json file currently holds our dependencies and scripts. It contains command for starting development server, updating webdriver and starting webdriver (mentioned just before this) and command to run test. Next we need to include a configuration file for protractor. The configuration file should contain the test framework to be used, the address at which selenium is running and path to specs file. // conf.js exports.config = { framework: "jasmine", seleniumAddress: "http://localhost:4444/wd/hub", specs: ["tests/home-spec.js"] }; We have set the framework as jasmine and selenium address as http://localhost:4444/wd/hub. Next we need to define our actual file. But before writing tests we need to find out what are the things that we need to test. We will mostly be testing dynamic content loaded by Javascript files. Let us define a spec. A spec is a collection of tests. We will start by testing the category name. Initially when the page loads it should be equal to All apps. Next we test the top right hand side menu which is loaded by javascript using topmenu.json file. it("should have a category name", function()…

Continue ReadingUsing Protractor for UI Tests in Angular JS for Loklak Apps Site

Keeping Order of tickets in Event Wizard in Sync with API on Open Event Frontend

This blog article will illustrate how the various tickets are stored and displayed in order the event organiser decides  on  Open Event Frontend and also, how they are kept in sync with the backend. First we will take a look at how the user is able to control the order of the tickets using the ticket widget. {{#each tickets as |ticket index|}} {{widgets/forms/ticket-input ticket=ticket timezone=data.event.timezone canMoveUp=(not-eq index 0) canMoveDown=(not-eq ticket.position (dec data.event.tickets.length)) moveTicketUp=(action 'moveTicket' ticket 'up') moveTicketDown=(action 'moveTicket' ticket 'down') removeTicket=(confirm 'Are you sure you wish to delete this ticket ?' (action 'removeTicket' ticket))}} {{/each}} The canMoveUp and canMoveDown are dynamic properties and are dependent upon the current positions of the tickets in the tickets array.  These properties define whether the up or down arraow or both should be visible alongside the ticket to trigger the moveTicket action. There is an attribute called position in the ticket model which is responsible for storing the position of the ticket on the backend. Hence it is necessary that the list of the ticket available should always be ordered by position. However, it should be kept in mind, that even if the position attribute of the tickers is changed, it will not actually change the indices of the ticket records in the array fetched from the API. And since we want the ticker order in sync with the backend, i.e. user shouldn’t have to refresh to see the changes in ticket order, we are going to return the tickets via a computed function which sorts them in the required order. tickets: computed('data.event.tickets.@each.isDeleted', 'data.event.tickets.@each.position', function() {    return this.get('data.event.tickets').sortBy('position').filterBy('isDeleted', false);  }) The sortBy method ensures that the tickets are always ordered and this computed property thus watches the position of each of the tickets to look out for any changes. Now we can finally define the moveTicket action to enable modification of position for tickets. moveTicket(ticket, direction) {      const index = ticket.get('position');      const otherTicket = this.get('data.event.tickets').find(otherTicket => otherTicket.get('position') === (direction === 'up' ? (index - 1) : (index + 1)));      otherTicket.set('position', index);      ticket.set('position', direction === 'up' ? (index - 1) : (index + 1));    } The moveTicket action takes two arguments, ticket and direction. It temporarily stores the position of the current ticket and the position of the ticket which needs to be swapped with the current ticket.Based on the direction the positions are swapped. Since the position of each of the tickets is being watched by the tickets computed array, the change in order becomes apparent immediately. Now when the User will trigger the save request, the positions of each of the tickets will be updated via a PATCH or POST (if the ticket is new) request. Also, the positions of all the tickets maybe affected while adding a new ticket or deleting an existing one. In case of a new ticket, the position of the new ticket should be initialised while creating it and it should be below all the other tickets. addTicket(type, position) {      const salesStartDateTime = moment();      const salesEndDateTime = this.get('data.event.startsAt');…

Continue ReadingKeeping Order of tickets in Event Wizard in Sync with API on Open Event Frontend

Implementing Order Statistics API on Tickets Route in Open Event Frontend

The order statistics API endpoints are used to display the statistics related to tickets, orders, and sales. It contains the details about the total number of orders, the total number of tickets sold and the amount of the sales. It also gives the detailed information about the pending, expired, placed and completed orders, tickets, and sales. This article will illustrate how the order statistics can be displayed using the Order Statistics API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for statistics is GET /v1/events/{event_identifier}/order-statistics First, we need to create a model for the order statistics, which will have the fields corresponding to the API, so we proceed with the ember CLI command: ember g model order-statistics-tickets Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the model looks like this: import attr from 'ember-data/attr'; import ModelBase from 'open-event-frontend/models/base'; export default ModelBase.extend({ orders : attr(), tickets : attr(), sales : attr() }); As we need to display the statistics related to orders, tickets, and sales so we have their respective variables inside the model which will fetch and store the details from the API. Now, after creating a model, we need to make an API call to get the details. This can be done using the following: return this.modelFor('events.view').query('orderStatistics', {}); Since the tickets route is nested inside the event.view route so, first we are getting the model for event.view route and then we’re querying order statistics from the model. The complete code can be seen here. Now, we need to call the model inside the template file to display the details. To fetch the total orders we can write like this {{model.orders.total}}   In a similar way, the total sales can be displayed like this. {{model.sales.total}}   And total tickets can be displayed like this {{model.tickets.total}}   If we want to fetch other details like the pending sales or completed orders then the only thing we need to replace is the total attribute. In place of total, we can add any other attribute depending on the requirement. The complete code of the template can be seen here. The UI for the order statistics on the tickets route looks like this. Fig. 1: The user interface for displaying the statistics The complete source code can be seen here. Resources: Open Event API Docs Official Ember Data documentation An article on how to create GET requests in ember in the blog by Adam Sommer

Continue ReadingImplementing Order Statistics API on Tickets Route in Open Event Frontend

Implementing Pages API in Open Event Frontend

The pages endpoints are used to create static pages which 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. This article will illustrate how the pages can be added or removed from the /admin/content/pages route using the pages API in Open Event Frontend. The primary end point of Open Event API with which we are concerned with for pages is GET /v1/pages First, we need to create a model for the pages, which will have the fields corresponding to the API, so we proceed with the ember CLI command: ember g model page Next, we need to define the model according to the requirements. The model needs to extend the base model class. The code for the page model looks like this: import attr from 'ember-data/attr'; import ModelBase from 'open-event-frontend/models/base'; export default ModelBase.extend({ name : attr('string'), title : attr('string'), url : attr('string'), description : attr('string'), language : attr('string'), index : attr('number', { defaultValue: 0 }), place : attr('string') }); As the page will have name, title, url which will tell the URL of the page, the language, the description, index and the place of the page where it has to be which can be either a footer or an event. The complete code for the model can be seen here. Now, after creating a model, we need to make an API call to get and post the pages created. This can be done using the following: return this.get('store').findAll('page'); The above line will check the store and find all the pages which have been cached in and if there is no record found then it will make an API call and cache the records in the store so that when called it can return it immediately. Since in the case of pages we have multiple options like creating a new page, updating a new page, deleting an existing page etc. For creating and updating the page we have a form which has the fields required by the API to create the page.  The UI of the form looks like this. Fig. 1: The user interface of the form used to create the page. Fig. 2: The user interface of the form used to update and delete the already existing page The code for the above form can be seen here. Now, if we click the items which are present in the sidebar on the left, it enables us to edit and update the page by displaying the information stored in the form and then the details be later updated on the server by clicking the Update button. If we want to delete the form we can do so using the delete button which first shows a pop up to confirm whether we actually want to delete it or not. The code for displaying the delete confirmation pop up looks like this. <button class="ui red button" {{action (confirm (t 'Are you sure you would like to delete…

Continue ReadingImplementing Pages API in Open Event Frontend

Implementing Skill Detail Section in SUSI Android App

SUSI Skills are rules that are defined in SUSI Skill Data repo which are basically the responses SUSI gives to the user queries. When a user queries something from the SUSI Android app, a query to SUSI Server is made which further fetches response from SUSI Skill Data and gives the response to the app. Similarly, when we need to list all skills, an API call is made to server to list all skills. The server then checks the SUSI Skill Data repo for the skills and then return all the required information to the app. Then the app displays all the information about the skill to user. User then can view details of each skill and then interact on the chat interface to use that skill. This process is similar to what SUSI Skill CMS does. The CMS is a skill wiki like interface to view all skills and then edit them. Though the app can not be currently used to edit the skills but it can be used to view them and try them on the chat interface. API Information For listing SUSI Skill groups, we have to call on /cms/getGroups.json This will give you all groups in SUSI model in which skills are present. Current response: { "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }}, "accepted": true, "groups": [ "Small Talk", "Entertainment", "Problem Solving", "Knowledge", "Assistants", "Shopping" ], "message": "Success: Fetched group list" } So, the groups object gives all the groups in which SUSI Skills are located. Next comes, fetching of skills. For that the endpoint is /cms/getGroups.json?group=GROUP_NAME Since we want all skills to be fetched, we call this api for every group. So, for example we will be calling http://api.susi.ai/cms/getSkillList.json?group=Entertainment for getting all skills in group “Entertainment”. Similarly for other groups as well. Sample response of skill: { "accepted": true, "model": "general", "group": "Shopping", "language": "en", "skills": {"amazon_shopping": { "image": "images/amazon_shopping.png", "author_url": "https://github.com/meriki", "examples": ["Buy a dress"], "developer_privacy_policy": null, "author": "Y S Ramya", "skill_name": "Shop At Amazon", "dynamic_content": true, "terms_of_use": null, "descriptions": "Searches items on Amazon.com for shopping", "skill_rating": null }}, "message": "Success: Fetched skill list", "session": {"identity": { "type": "host", "name": "14.139.194.24", "anonymous": true }} } It gives all details about skills: image author_url examples developer_privacy_policy author skill_name dynamic_content terms_of_use descriptions skill_rating Implementation in SUSI Android App Skill Detail Section UI of Google Assistant Skill Detail Section UI of SUSI SKill CMS Skill Detail Section UI of SUSI Android App The UI of skill detail section in SUSI Android App is the mixture of UI of Skill detail section in Google Assistant ap and SUSI Skill CMS. It displays details of skills in a beautiful manner with horizontal recyclerview used to display the examples. So, we have to display following details about the skill in Skill Detail Section: Skill Name Author Name Skill Image Try it Button Description Examples Rating Content type (Dynamic/Static) Terms of Use Developer’s Privacy policy Let’s see the implementation. 1. Whenever a skill Card View is clicked, showSkillDetailFragment()…

Continue ReadingImplementing Skill Detail Section in SUSI Android App

Link Preview Service from SUSI Server

 SUSI Webchat, SUSI Android app, SUSI iOS app are various SUSI clients which depend on response from SUSI Server. The most common response of SUSI Server is in form of links. Clients usually need to show the preview of the links to the user. This preview may include featured image, description and the title of the link.  Clients show this information by using various 3rd party APIs and libraries. We planned to create an API endpoint for this on SUSI Server to give the preview of the link. This service is called LinkPreviewService. String url = post.get("url", ""); if(url==null || url.isEmpty()){ jsonObject.put("message","URL Not given"); jsonObject.put("accepted",false); return new ServiceResponse(jsonObject); } This API Endpoint accept only 1 get parameter which is the URL whose preview is to be shown. Here we also check if no parameter or wrong URL parameter was sent. If that was the the case then we return an error message to the user. SourceContent sourceContent = TextCrawler.scrape(url,3); if (sourceContent.getImages() != null) jsonObject.put("image", sourceContent.getImages().get(0)); if (sourceContent.getDescription() != null) jsonObject.put("descriptionShort", sourceContent.getDescription()); if(sourceContent.getTitle()!=null)jsonObject.put("title", sourceContent.getTitle()); jsonObject.put("accepted",true); return new ServiceResponse(jsonObject); } The TextCrawler function accept two parameters. One is the url of the website which is to be scraped for the preview data and the other is depth. To get the images, description and title there are methods built in. Here we just call those methods and set them in our JSON Object. private String htmlDecode(String content) { return Jsoup.parse(content).text(); } Text Crawler is based on Jsoup. Jsoup is a java library that is used to scrape HTML pages. To get anything from Jsoup we need to decode the content of HTML to Text. public List<String> getImages(Document document, int imageQuantity) { Elements media = document.select("[src]"); while(var5.hasNext()) { Element srcElement = (Element)var5.next(); if(srcElement.tagName().equals("img")) { ((List)matches).add(srcElement.attr("abs:src")); } }  The getImages method takes the HTML document from the JSoup and find the image tags in that. We have given the imageQuantity parameter in the function, so accordingly it returns the src attribute of the first n images it find. This API Endpoint can be seen working on http://127.0.0.1:4000/susi/linkPreview.json?url=<ANY URL> A real working example of this endpoint would be http://api.susi.ai/susi/linkPreview.json?url=https://techcrunch.com/2017/07/23/dear-tech-dudes-stop-being-such-idiots-about-women/ Resources: Web Crawlers: https://www.promptcloud.com/data-scraping-vs-data-crawling/ JSoup: https://jsoup.org/ JSoup Api Docs: https://jsoup.org/apidocs/ Parsing HTML with JSoup: http://www.baeldung.com/java-with-jsoup

Continue ReadingLink Preview Service from SUSI Server

Implementing Notifications in Open Event Server

In FOSSASIA’s Open Event Server project, along with emails, almost all actions have necessary user notifications as well. So, when a new session is created or a session is accepted by the event organisers, along with the email, a user notification is also sent. Though showing the user notification is mainly implemented in the frontend site but the content to be shown and on which action to show is strictly decided by the server project. A notification essentially helps an user to get the necessary information while staying in the platform itself and not needing to go to check his/her email for every action he performs. So unlike email which acts as a backup for the informations, notification is more of an instant thing. The API The Notifications API is mostly like all other JSON API endpoints in the open event project. However in Notifications API we do not allow any to send a POST request. The admin of the server is able to send a GET a request to view all the notifications that are there in the system while a user can only view his/her notification. As of PATCH we allow only the user to edit his/her notification to mark it as read or not read. Following is the schema for the API: class NotificationSchema(Schema): """ API Schema for Notification Model """ class Meta: """ Meta class for Notification API schema """ type_ = 'notification' self_view = 'v1.notification_detail' self_view_kwargs = {'id': '<id>'} self_view_many = 'v1.microlocation_list_post' inflect = dasherize id = fields.Str(dump_only=True) title = fields.Str(allow_none=True, dump_only=True) message = fields.Str(allow_none=True, dump_only=True) received_at = fields.DateTime(dump_only=True) accept = fields.Str(allow_none=True, dump_only=True) is_read = fields.Boolean() user = Relationship(attribute='user', self_view='v1.notification_user', self_view_kwargs={'id': '<id>'}, related_view='v1.user_detail', related_view_kwargs={'notification_id': '<id>'}, schema='UserSchema', type_='user' ) The main things that are shown in the notification from the frontend are the title and message. The title is the text that is shown without expanding the entire notification that gives an overview about the message in case you don’t want to read the entire message. The message however provides the entire detail that is associated with the action performed. The user relationship stores which user the particular notification is related with. It is a one-to-one relationship where one notification can only belong to one user. However one user can have multiple notifications. Another important attribute is the is_read attribute. This is the only attribute that is allowed to be changed. By default, when we make an entry in the database, is_read is set to FALSE. Once an user has read the notification, a request is sent from the frontend to change is_read to TRUE. The different actions for which we send notification are stored in the models/notification.py file as global variables. USER_CHANGE_EMAIL = "User email"' NEW_SESSION = 'New Session Proposal' PASSWORD_CHANGE = 'Change Password' EVENT_ROLE = 'Event Role Invitation' TICKET_PURCHASED = 'Ticket(s) Purchased' TICKET_PURCHASED_ATTENDEE = 'Ticket(s) purchased to Attendee ' EVENT_EXPORTED = 'Event Exported' EVENT_EXPORT_FAIL = 'Event Export Failed' EVENT_IMPORTED = 'Event Imported' HTML Templates The notification title and message that is stored in the…

Continue ReadingImplementing Notifications in Open Event Server