Working with Logic Analyzer in PSLab application

This blog demonstrates the working of Logic Analyzer instrument available in PSLab Desktop Application. It also includes a detailed description of the features available in the Logic Analyzer instrument. Also, it provides a step by step guide on how to work with the Logic Analyzer provided by PSLab which will be beneficial to first-time users.

What is a Logic Analyzer?

A Logic Analyzer is an electronic instrument used to capture and display digital signals with an added functionality of providing the time difference between different edges of different pulses. It is mainly used to observe the time relationship between different digital signals. An example of a standard Logic Analyzer available in the market is as shown in figure 1.

Figure 1. Standard Logic Analyzer

How to generate different digital pulses in PSLab?

Logic Analyzer needs to be provided with some input of digital pulses among whom time relationship is to be found out. Digital pulses generated from different systems can be directly provided as input to the logic analyzer for analyzing. But PSLab provides a functionality to generate digital pulses up to some constrained frequency.

Following are the steps to generate different digital waves in the PSLab desktop application :

  • Go to Advanced Control Section of PSLab app. The screen should look like one as shown in Figure 2.

Figure 2.  Advanced Control Section

  • PSLab device provides generation of maximum four digital waves at once. In this example, I will proceed by utilizing all the four pins i.e. SQR1, SQR2, SQR3, SQR4 (where SQR = Acronym of square wave generator and the number next to it is the pin ID available on the PSLab device). Set the duty cycles for each of the pins as desired (try to keep all the duty cycles different from each other to understand the process of measurement easily). After setting it should look something like Figure 3.

Figure 3. Configuring PWM

NOTE: User can also set phase angle for different waves but I will proceed keeping all without any phase difference.

  • Now set the frequency of the digital waves in the tab provided next to text Frequency and then press the SET button. This should generate desired digital waves when connected.

How to analyze the generated waves in Logic Analyzer?

  • Now go to the Experiments section and click on the Logic Analyzer instrument as shown in Figure 4.

Figure 4. Test and Measurement Page

  • Now a screen as shown in Figure 5 should open which is the main screen for Logic Analyzer Instrument.

Figure 5. Logic Analyzer Main Page

On the right, you can see three buttons i.e Start, Plot Data and Raw Data. Below that selection for the number of channels is provided. And at last, the time measurement tool is provided which can measure the time difference between different edges of different digital waves. The graph at the center is the place where all the waves generated will be plotted.

  • Now as we have generated four different waves, we need to navigate to Four Channel Mode in the Channel Selection section. There you can observe four pins i.e. ID1, ID2, ID3, and ID4 are selected by default as shown in Figure 6. So, we need to connect the pins SQR1, SQR2, SQR3 and SQR4 with pins ID1, ID2, ID3 and ID4 on the PSLab device.

Figure 6. Channel Selection Section

NOTE: There are several options available for plotting the digital waves besides the one selected in the above image i.e.

  1. Every Edge – Plot every edge of the signal
  2. Falling Edges – Plot only falling edges of the signal (When a signal comes from 1 to 0 state)
  3. Rising Edges Only – Plot only rising edges of the signal (When a signal comes from 0 to 1 state)
  4. Disabled – Don’t plot the selected wave

Figure 7. Connecting wires on PSLab device

  • Connect the wires on the PSLab device as shown in Figure 7.
  • Now, as the device is connected, we can use the three buttons located at the top right corner to plot the digital waves. The functionalities of all the three buttons are as follows:
  1. Start – It collects exactly 2500 sample points from the wave generated to be plotted
  2. Plot Data – It plots the data on the graph if the samples are collected successfully
  3. Raw Data – It provides a sheet containing time at particular intervals and the difference between the data points at that particular time
  • Press the Start button and as soon as the samples are collected, press the Plot Data button. Then the screen should look something like Figure 8.

Figure 8. Final graph of input provided

Zoom in further, and the screen will look like Figure 9.

Figure 9. Zoomed In graph for better visualization

  • So now, the waves are plotted and are ready to be analyzed. Time difference between any two edges for any two distinct or a similar wave can be found out by using the TIME INTERVAL MEASUREMENT TOOL which is as shown in Figure 10.

Figure 10. Time Interval Measurement Tool

  • The working of this tool can be explained by taking an example as follow :

Figure 11. Configuring the Time Interval Measurement Tool

Suppose time difference between the rising edge of ID1 and falling edge of ID3 is to be measured. So, set the parameters as shown in Figure 11 for the following situation.

The timeout of the Logic Analyzer can be set to any desired value from 10mS to 9999 mS. Here, I will proceed with 10mS of the timeout. Now, press the measure button to see the result. It will be same as the Figure 12 shown below if all the configurations are set as the one in the  example :

Figure 12. Calculated time difference displayed

So in this way, many calculations can be taken out very easily and can be viewed at once at a single place without manually doing any calculations. Also, the data generated can be stored in the local system by pressing the Save Data button located at the bottom right corner.

Resources

  1. PSLab Desktop Application – https://github.com/fossasia/pslab-desktop-apps (Link to repo)
  2. PSLab device pins sticker – https://github.com/fossasia/pslab-artwork/blob/master/Sticker/pslabdesign.png

 

Continue ReadingWorking with Logic Analyzer in PSLab application

Parallelizing travis build in Open Event Web app

 

Open Event Web app uses Travis CI as a platform to perform unit testing. Travis CI is a hosted, distributed continuous integration service used to build and test projects hosted at GitHub. Travis CI automatically detects when a commit has been made and pushed to a GitHub repository that is using Travis CI, and each time this happens, it will try to build the project and run tests. Travis build took around 24 minutes to complete when any commit is made to the project, which is a very large time, to reduce the build time we parallelized the build which uses maximum amount of resources available at that time and run the builds parallely which resulted into better use of resources as well as required lesser amount of time.

Open Event web app uses saucelabs integration to perform selenium tests and travis to perform continuous integration.

Why parallelize the build?

When there are unit tests that are independent of each other and can be executed using a common set of dependencies, those procedures can be performed parallely on different virtual machines bringing out maximum throughput.

Running say a large number of tests on a single machine can increase the build time to a large extent, this build time can be reduced significantly by running the tests parallely on different machines. Open Event Webapp has a build time of around 24 minutes which is reduced to half on parallelising the build.

Parallelizing your builds across virtual machines

To speed up a test suite, you can break it up into several parts using Travis CI’s build matrix feature.

Say you want to split up your unit tests and your integration tests into two different build jobs. They’ll run in parallel and fully utilize the available build capacity and the resources.

The architecture of open event webapp supports test suite for all the pages in the generated application. To parallelize the build, the test suite is divided in different files  with the directory structure as shown below:

   
   ├── test
      ├── serverTest.js
      ├── roomsAndSpeakers.js
      ├── tracks.js
      ├── generatorAndSchedule.js
      ├── sessionAndEvent,js

 

The env key in travis.yml is modified as shown below:

env:
  - TESTFOLDER=test/serverTest.js
  - TESTFOLDER=test/roomsAndSpeakers.js
  - TESTFOLDER=test/tracks.js
  - TESTFOLDER=test/generatorAndSchedule.js
  - TESTFOLDER=test/sessionAndEvent.js

 

The script running tests fetches environment variable and runs the test file accordingly as shown below:

# installing required items for build
install:
 - npm install -g istanbul mocha@3
 - npm install
 - npm install --save-dev

# testing script
script:
 - istanbul cover _mocha -- $TESTFOLDER

# notify codecov and deploy to cloud
after_success:
 if ([ "$TESTFOLDER" == "test/serverTest.js" ]); then
   bash <(curl -s https://codecov.io/bash);
   bash gh_deploy.sh && kubernetes/travis/deploy.sh;
 fi

Results:

The build time which was earlier 23 minutes is reduced to 12 minutes after parallelizing the build.

Resources

 

Continue ReadingParallelizing travis build in Open Event Web app

Making Tree View in SUSI botbuilder

SUSI botbuilder enables you to create your own bot which you can integrate in your own website. While creating the skill for the bot, you often need to visualize the dialog flow. The Tree View helps you to visualize the conversation flow. The queries from the user and the related responses from the bot can easily be understood by seeing the tree.

Installing package

We will be using react-orgchart to make the tree. React-orgchart offers minimum basic implementation of the tree and we can easily customise the nodes. To install the package, type the following command in your terminal:

npm i react-orgchart --save

 

Using the Orgchart

In the TreeView.js file, we import the orgchart and render it. The tree data and node component is passed to the OrgChart component. Tree data stores the format and conversations in the tree and the node component represents the rendering of the individual node. We can customise the node according to our needs.

const MyNodeComponent = ({node}) => {
      return (
‘initechNode’>{node.type===‘bot’ && ‘#4285f5’ style={styles.icon}/>}{node.type===‘user’ && } { node.name }

); }; <OrgChart tree={this.props.treeData} NodeComponent={MyNodeComponent} />

 

Generating the Tree Data

The data for the tree is generated by the code view. As user adds or edit skills in the code view, the skill language is interpreted and converted to a tree data structure. The generateTreeData() function in Build.js file does this job.

//generation of skills from the code view
for (let i = 0; i < lines.length; i++) {
  let bot_response = null;
  let line = lines[i];
  if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) {
    let user_query = line;
  while (true) {
    i++;
    if (i >= lines.length) {
      break;
    }
    line = lines[i];
    if (line && !line.startsWith('::') && !line.startsWith('!') && !line.startsWith('#')) {
      bot_response = lines[i];
    break;
  }
}
let obj = {
  user_query,
  bot_response,
};
skills.push(obj);
}
}

 

Thus, we are separating the skills in the code view and separating the bot and user response. A sample Tree data looks like:

const treeData = {
  name: 'Welcome!',
  children: [
    {
      name: 'User query 1',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    },
    {
      name: 'User query 2',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    },
    {
      name: 'User query 3',
      type: 'user',
      children: [
        {
          name: 'Answer for the user query',
          type: 'bot',
        }
      ]
    }
  ]
}

 

Result:

Resources

Continue ReadingMaking Tree View in SUSI botbuilder

Add auto copy code feature in botbuilder

SUSI botbuilder lets you create your own skill bot and deploy on your website. After you have customised your bot, you will get a javascript code that you need to paste in your website’s source code. To make the process of copying the code easy, we have developed a feature for auto copying of the code to your clipboard. You just need to click on a button “copy” and the code will be copied to your clipboard.

Installing package

We use react-copy-to-clipboard package to enable auto copy feature. Install it in your project using the following command.

npm i react-copy-to-clipboard --save

 

Adding code inside render function

Inside the render() function in react file, paste the following code where you want the copy button to be displayed. Here we need to provide the text to be copied to the CopyToClipboard component via the text props. We pass the script code. We get the access token for the bot via the saved cookie. Inside the children of CopyToClipboard we need to pass the copy button which we want to show.

<CopyToClipboard
  text={
    " +cookies.get('uuid') +"' data-group='" +
    group + "' data-language='" + language + "' data-skill='" + skill + "' src='" + api + "/susi-chatbot.js'>"
  }
  onCopy={() => this.setState({ copied: true })}
>
  <span className="copy-button">copy</span>
</CopyToClipboard>

 

Thus, when the user clicks on the “copy” button, the code will be automatically copied to the user’s clipboard.

Showing snackbar message

After the code has been copied to the user’s clipboard, we can show a snackbar message to inform the user. First we pass a function onCopy to the CopyToClipboard component. This sets the state variable copied to true. Then we have a snackbar component which displays the message.

<Snackbar
   open={this.state.copied}
   message="Copied to clipboard!"
   autoHideDuration={2000}
   onRequestClose={() => {
      this.setState({ copied: false });
   }}
/>

 

Result:

 

Resources

 

Continue ReadingAdd auto copy code feature in botbuilder

Create custom theme for SUSI chatbot web plugin

SUSI web plugin bot provides the features of SUSI as a chat window which users can plugin to their websites. They just need to copy the generated javascript code into their website and the SUSI bot plugin will be enabled. The user can customize the theme of the chatbot. This blog explains how the feature of applying custom theme is implemented.

Accepting user’s input

The react component of our concern is BotBuilderPages/Design.js. We use material-ui-color-picker component to accept user’s choice of color.

<ColorPicker
  className='color-picker'
  style={{display:'inline-block',float:'left'}}
  name='color'
  defaultValue={ this.state[component.component] }
  onChange={(color)=>
   this.handleChangeColor(component.component,color) }
/>

 

Similarly, there is a text field which accepts the url of an image. The values are stored in the component’s state variables.

Storing settings in server

The design settings are stored in the text format:

This forms part of the skill’s text file. When the user clicks on “save and deploy” button, the complete skill gets send to the server through the following API:

let settings = {
      async: true,
      crossDomain: true,
      url:
        urls.API_URL +
        '/cms/' +
        (this.state.updateSkillNow ? 'modifySkill.json' : 'createSkill.json'),
      method: 'POST',
      processData: false,
      contentType: false,
      mimeType: 'multipart/form-data',
      data: form,
    };

    $.ajax(settings)
      .done(function(response) {
        // successfully saved in server
      });

 

In the server, the skill is saved in susi_private_skill_data directory. Also, the design and configuration settings are stored in chatbot.json file.

Applying the settings to the bot

Now, in the susi-chatbot.js file, the custom theme settings are applied to the bot. The function getTheme() fetches the theme settings from the server via an ajax request. Then the function applyTheme() is executed which applies the theme to the chatbot.

$(".susi-sheet-content-container").css("background-color",botbuilderBackgroundBody);
if(botbuilderBodyBackgroundImg){
$(".susi-sheet-content-container").css("background-image","url("+botbuilderBodyBackgroundImg+")");
}

 

Similarly, other theme variables are applied as well. Thus we have customised the theme of the SUSI chatbot plugin.

Result:

Resources

 

Continue ReadingCreate custom theme for SUSI chatbot web plugin

Enable web app generation for multiple API formats

 

 

 

 

 

Open event server has two types of API (Application Programming Interface) formats, with one being generated by the legacy server and other one by the server side of decoupled development structure. The open event web app supported only the new version of API format, thus an error in read contents of JSON was thrown for the old version API format. To enable the support for both kind of API formats such that web app can be generated for each of them and there is no need to convert JSON files of version v1 to v2 we added an option field to the generator, where the client can choose the API version.

Excerpts and description for difference between data formats of API v1 and v2

The following excerpt is a subprogram getCopyrightData in both versions v1 and v2. The key for getting licence details in v1 is ‘licence_details’ and in v2 is ‘licence-details’. Similarly the key for getting copyright details in v1 is ‘copyright’ and in v2 is ‘event-copyright’.

So the data is extracted from the JSON files depending on the API version, the client has selected.

API V1

function getCopyrightData(event) {
 if(event.licence_details) {
   return convertLicenseToCopyright(event.licence_details, event.copyright);
 } else {
   return event.copyright;
 }
}

 

API V2

function getCopyrightData(event) {
 if(event['licence-details']) {
   return convertLicenseToCopyright(event['licence-details'], event['event-copyright']);
 } else {
   event['event-copyright'].logo = event['event-copyright']['logo-url'];
   return event['event-copyright'];
 }
}

 

Another example showing the difference between the API formats of v1 and v2 is excerpted below.

The following excerpt shows a constant ‘url’ containing the event URLs and the details. The version v1 uses event_url as a key for the main page url whereas v2 uses event-url for getting the same. A similar kind of structural difference is present for rest of the fields where the special character underscore has been replaced by hyphen and a bit of change in the name format for keys such as start_time, end_time.

API v1

const urls= {
 main_page_url: event.event_url,
 logo_url: event.logo,
 background_url: event.background_image,
 background_path: event.background_image,
 description: event.description,
 location: event.location_name,
 orgname: event.organizer_name,
 location_name: event.location_name,
};

 

API v2

const urls= {
 main_page_url: event['event-url'],
 logo_url: event['logo-url'],
 background_url: event['original-image-url'],
 background_path: event['original-image-url'],
 location: event['location-name'],
 orgname: event['organizer-name'],
 location_name: event['location-name'],
};

How we enabled support for both API formats?

To add the support for both API formats we added a options field on generator’s index page where the user chooses the type of API format for web app generation.

<label>Choose your API version</label>
<ul style="list-style-type:none">
 <li id="version1"><input name="apiVersion" type="radio" value="api_v1">   API_v1</li>
 <li id="version2"><input name="apiVersion" type="radio" value="api_v2"> API_v2</li>
</ul>

The generator depending on the version of API format, chooses the correct file where the data extraction from the input JSON files takes place. The file names are fold_v1.js and fold_v2.js for extraction of JSON v1 data and JSON v2 data respectively.

var type = req.body.apiVersion || 'api_v2';

if(type === 'api_v1') {
 fold = require(__dirname + '/fold_v1.js');
}
else {
 fold = require(__dirname + '/fold_v2.js');
}

 

The excerpts of code showing the difference between API formats of v1 and v2 are the contents of fold_v1.js and fold_v2.js files respectively.

Resources

Continue ReadingEnable web app generation for multiple API formats

Submitting a Github Issue through a Google Form

The Pocket Science Lab Android app has various functionalities which have been already implemented but it been on the verge of development, many functionalities are yet to be implemented completely, one such functionality is how the users report the issues of the app, to which comes the idea of using a Google form inside the app for the users to fill it and the issue get directly opened in Github.

Submitting a Github issue through a Google forms requires two things:-

    1. A Github access token which gives access to open a new issue.
      • To generate a Github access token one must follow these steps[2]
        • Go to the personal settings.

        • Select  Developers settings option from it.
        • In Developers settings option Go to personal access tokens and generate an access token.
    1. A fully-structured Google form which has all the details about the issue i.e the title of the issue, the body of the issue, label, etc..
      • Using a Google account create a Google Form which have all the relevant questions about that issue such as title of the issue, body of the issue, label etc..

Once done with all the steps follow these steps to send a Github issue[1]

    1. Click the Responses tab, in it click the More icon.
    2. Select Choose a response destination.
    3. Select New spreadsheet: Creates a new spreadsheet in Google Sheets for responses.
    4. Click Create to create and open the sheet.

Configure the App Script Logic[1]

    1. You should have a newly created blank spreadsheet with headers automatically generated from your form.
    2. Click Tools > Script editor… to launch the App Script editor coding environment. This Script will be bound to your sheet, so you can listen for form submissions and fire off a new issue to your GitHub repo.
    3. In the script editor write the following code
function onFormSubmit(e) {

var title = e.values[1];
var body = e.values[2];
var label = "User opened issue"
var payload = {
"title": title,
"body": a_body,
"label": label,
};

var options = {
"method": "POST",
"contentType": "application/json",
"payload": JSON.stringify(payload)
};
var response = UrlFetchApp.fetch("https://api.github.com/repos/abhinavraj23/AgeGroup/issues?access_token="+ghToken, options)
}

Note:The onFormSubmit function includes an event object e, which includes the form/spreadsheet field values as a simple array with values in the same order as they appear in the spreadsheet. e.values[0] is the first spreadsheet column

The following google-app script uses GitHub Issues API for posting a new issue in Github.

4.Give your app script project a name and save it .

Set up the Trigger[1]

        1. From within the app script editor, click Resources > Current project’s triggers.
        2. Click to add a trigger
          1. Run: onFormSubmit
          2. Events: From spreadsheet, On form submit
        3. Click Save and accept any authorizations to access your forms and access web services on your behalf.
        4. This trigger will listen to form submissions and pass the data to your function, which POSTs the new issue to your GitHub repo.

Thus using these steps one can submit an issue in github through a Google Form and thus the Google Forms can be used in the app as the users can send the issues using a google form, and through this method one can also get the email-id of the user for further contact and thus this is a very  useful method.

Resources

      1. Bmcbride, google-form-to-github-issue,gist.github.com: https://gist.github.com/bmcbride/62600e48274961819084#set-up-the-trigger
      2. Github help, Creating personal access token,help.github.com: https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/
Continue ReadingSubmitting a Github Issue through a Google Form

Open Event Server – Export Attendees as CSV File

FOSSASIA‘s Open Event Server is the REST API backend for the event management platform, Open Event. Here, the event organizers can create their events, add tickets for it and manage all aspects from the schedule to the speakers. Also, once he/she makes his event public, others can view it and buy tickets if interested.

The organizer can see all the attendees in a very detailed view in the event management dashboard. He can see the statuses of all the attendees. The possible statuses are completed, placed, pending, expired and canceled, checked in and not checked in. He/she can take actions such as checking in the attendee.

If the organizer wants to download the list of all the attendees as a CSV file, he or she can do it very easily by simply clicking on the Export As and then on CSV.

Let us see how this is done on the server.

Server side – generating the Attendees CSV file

Here we will be using the csv package provided by python for writing the csv file.

import csv
  • We define a method export_attendees_csv which takes the attendees to be exported as a CSV file as the argument.
  • Next, we define the headers of the CSV file. It is the first row of the CSV file.
def export_attendees_csv(attendees):
   headers = ['Order#', 'Order Date', 'Status', 'First Name', 'Last Name', 'Email',
              'Country', 'Payment Type', 'Ticket Name', 'Ticket Price', 'Ticket Type']
  • A list is defined called rows. This contains the rows of the CSV file. As mentioned earlier, headers is the first row.
rows = [headers]
  • We iterate over each attendee in attendees and form a row for that attendee by separating the values of each of the columns by a comma. Here, every row is one attendee.
  • The newly formed row is added to the rows list.
for attendee in attendees:
   column = [str(attendee.order.get_invoice_number()) if attendee.order else '-',
             str(attendee.order.created_at) if attendee.order and attendee.order.created_at else '-',
             str(attendee.order.status) if attendee.order and attendee.order.status else '-',
             str(attendee.firstname) if attendee.firstname else '',
             str(attendee.lastname) if attendee.lastname else '',
             str(attendee.email) if attendee.email else '',
             str(attendee.country) if attendee.country else '',
             str(attendee.order.payment_mode) if attendee.order and attendee.order.payment_mode else '',
             str(attendee.ticket.name) if attendee.ticket and attendee.ticket.name else '',
             str(attendee.ticket.price) if attendee.ticket and attendee.ticket.price else '0',
             str(attendee.ticket.type) if attendee.ticket and attendee.ticket.type else '']

   rows.append(column)
  • rows contains the contents of the CSV file and hence it is returned.
return rows
  • We iterate over each item of rows and write it to the CSV file using the methods provided by the csv package.
writer = csv.writer(temp_file)
from app.api.helpers.csv_jobs_util import export_attendees_csv
content = export_attendees_csv(attendees)
for row in content:
   writer.writerow(row)

Obtaining the Attendees CSV file:

Firstly, we have an API endpoint which starts the task on the server.

GET - /v1/events/{event_identifier}/export/attendees/csv

Here, event_identifier is the unique ID of the event. This endpoint starts a celery task on the server to export the attendees of the event as a CSV file. It returns the URL of the task to get the status of the export task. A sample response is as follows:

{
  "task_url": "/v1/tasks/b7ca7088-876e-4c29-a0ee-b8029a64849a"
}

The user can go to the above-returned URL and check the status of his/her Celery task. If the task completed successfully he/she will get the download URL. The endpoint to check the status of the task is:

and the corresponding response from the server –

{
  "result": {
    "download_url": "/v1/events/1/exports/http://localhost/static/media/exports/1/zip/OGpMM0w2RH/event1.zip"
  },
  "state": "SUCCESS"
}

The file can be downloaded from the above-mentioned URL.

References

Continue ReadingOpen Event Server – Export Attendees as CSV File

Implementing Card View in PSLab app

Card View was announced by Google in I/O ‘14 conference. Although it started a bit slow, but now we can see most of the apps like Snapchat, Google, Facebook, etc. using this widget. So, this blog is solely contributed on how to implement Card View in your own app by taking example of PSLab application.

What is Card View ?

CardView is a view container or ViewGroup that inherits its nature from FrameLayout which further inherits from ViewGroup itself. The only thing that separates a CardView from any other ViewGroups is its nature to behave like a card, more specifically the shadow and rounded corners. The basic customization that a CardView provides includes CornerRadius, Elevation, MaxElevation, ContentPadding, CompatPadding, PreventCornerOverlap, and a dedicated CardBackgroundColor or say Card Background which is the most necessary feature for a card to look cool.

Step by Step description how CardView was implemented in PSLab

  • First step is to add dependencies in your project as widgets like Card View, Recyclerview, etc. are not included in a common repository of widgets provided by Google.

App level dependency :

compile 'com.android.support:cardview-v7:26.0.0'
compile 'com.android.support:recyclerview-v7:+'

If you are using Android Studio 3.0+ than add the following dependency instead of above :

implementation 'com.android.support:cardview-v7:26.0.0'
implementation 'com.android.support:recyclerview-v7:27.1.1'
  •  Now we can use Card View widget in our app. So, first make add card view in layout section like this :
<android.support.v7.widget.CardView
    	android:id="@+id/card_view"
    	android:layout_width="match_parent"
    	android:layout_height="@dimen/total_height_card"
    	android:layout_gravity="center"
    	android:layout_margin="@dimen/card_margin"
    	card_view:cardCornerRadius="@dimen/card_radius">
 </android.support.v7.widget.CardView>

These are the basic attributes that are used while declaring a card view. Other possible attributes can be :

  1. Elevation – Used to elevate card to give a depth effect
  2. MaxElevation – Used to give constraint to the depth effect
  3. ContentPadding – Used to provide padding between content and the card
  4. PreventCornerOverlap – To prevent different corners to overlap as shown in figure 1.

Figure 1. Image showing corner overlapping in CardView

  • Now to set the objects inside the Card View, mostly RelativeLayout is preferred as it gives the freedom to place objects in reference of others whereas LinearLayout provides freedom to place them in only one direction. Other layouts such as FrameLayout, Tables, etc. can be used as per the need of the app.
  • Now we will create a layout that will hold all the cards using RecyclerView.
<android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
  • Now after setting the layouts, it’s time to make adapter which inflates the information in the cards which is then represented using RecyclerView.
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.Holder> {

	private List<ApplicationItem> applicationList;
	private final OnItemClickListener listener;
	/**
 	* View holder for application list item
 	*/
	public class Holder extends RecyclerView.ViewHolder {

    	TextView header, description;
    	ImageView applicationIcon; //Background Image

    	public Holder(View itemView) {
        super(itemView);
        this.header = itemView.findViewById(R.id.heading_card);
        this.description = itemView.findViewById(R.id.description_card);
        this.applicationIcon = itemView.findViewById(R.id.application_icon);
    	}

    	public void setup(final ApplicationItem applicationItem, final OnItemClickListener listener) {
      header.setText(applicationItem.getApplicationName());
      description.setText(applicationItem.getApplicationDescription());
      applicationIcon.setImageResource(applicationItem.getApplicationIcon());
         }
	}

	public ApplicationAdapter(Context mContext, List<ApplicationItem> applicationList, OnItemClickListener listener) {
    	this.mContext = mContext;
    	this.applicationList = applicationList;
    	this.listener = listener;
	}

     @Override
	public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
    	View itemView = LayoutInflater.from(parent.getContext())
            	.inflate(R.layout.application_list_item, parent, false);
    	return new Holder(itemView);
	}

Following is the detailed explanation of each and every method :

  1. Holder(View ) – As name suggests, it holds all the items that are included in a single card together
  2. setup() – This method can contain any number of parameters as per requirement. It basically sets the data in the views in the card
  3. ApplicationAdapter() – Constructor
  4. onCreateViewHolder() – It inflates the layout containing CardView as soon as the data gets ready to be fed in it
  • Now as adapter is ready, we can declare RecyclerView in Java code to implement CardView. The main reason to use RecyclerView is that it provides the feature of scrollability so that a number of cards can be adjusted on the screen. ScrollView can also be used but it slows down the app as it tries to load all the card at once rather than loading cards as per use like RecyclerView.
RecyclerView listView = view.findViewById(R.id.applications_recycler_view);
    	RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(context, rows);
    	listView.setLayoutManager(mLayoutManager);
    	listView.setItemAnimator(new DefaultItemAnimator());
    	listView.setAdapter(applicationAdapter);

Here, we have used GridLayoutManager to use grids along with RecyclerView. It is optional and can be used as per requirement. Also, before setting adapter, fill the adapter with relevant data or else no card will be seen in actual app.

  • Now the app is ready to be built and tested on mobile device. This is how it looks in the PSLab application after implementing the above guide :

Figure 2. Screenshot of CardView implemented in PSLab app

So, in this way great user experience can be given by using this very basic widget. But great attention should be given while designing the objects inside the card as the selection and position of the objects is what makes the card look good. Objects used in making cards for PSLab very well suited the app and so are designed like that. Practice should be done by taking reference of some very good apps like Snapchat, Google, etc. and from Material Designs provided by Google before implementing them in actual project.

Resources

  1. https://www.androidhive.info/2016/05/android-working-with-card-view-and-recycler-view/ – This article gives a better practice by implementing a real world example
  2. https://developer.android.com/reference/android/support/v7/widget/CardView – Official documentation by Google on CardView
  3. https://developer.android.com/guide/topics/ui/layout/recyclerview – Official documentation by Google on RecyclerView

Continue ReadingImplementing Card View in PSLab app

Open Event Server – Export Orders as CSV File

FOSSASIA‘s Open Event Server is the REST API backend for the event management platform, Open Event. Here, the event organizers can create their events, add tickets for it and manage all aspects from the schedule to the speakers. Also, once he/she makes his event public, others can view it and buy tickets if interested.

The organizer can see all the orders in a very detailed view in the event management dashboard. He can see the statuses of all the orders. The possible statuses are completed, placed, pending, expired and canceled.

If the organizer wants to download the list of all the orders as a CSV file, he or she can do it very easily by simply clicking on the Export As and then on CSV.

Let us see how this is done on the server.

Server side – generating the Orders CSV file

Here we will be using the csv package provided by python for writing the csv file.

import csv
  • We define a method export_orders_csv which takes the orders to be exported as a CSV file as the argument.
  • Next, we define the headers of the CSV file. It is the first row of the CSV file.
def export_orders_csv(orders):
   headers = ['Order#', 'Order Date', 'Status', 'Payment Type', 'Total Amount', 'Quantity',
              'Discount Code', 'First Name', 'Last Name', 'Email']
  • A list is defined called rows. This contains the rows of the CSV file. As mentioned earlier, headers is the first row.
rows = [headers]
  • We iterate over each order in orders and form a row for that order by separating the values of each of the columns by a comma. Here, every row is one order.
  • The newly formed row is added to the rows list.
for order in orders:
   if order.status != "deleted":
       column = [str(order.get_invoice_number()), str(order.created_at) if order.created_at else '',
                 str(order.status) if order.status else '', str(order.paid_via) if order.paid_via else '',
                 str(order.amount) if order.amount else '', str(order.get_tickets_count()),
                 str(order.discount_code.code) if order.discount_code else '',
                 str(order.user.first_name)
                 if order.user and order.user.first_name else '',
                 str(order.user.last_name)
                 if order.user and order.user.last_name else '',
                 str(order.user.email) if order.user and order.user.email else '']
       rows.append(column)
  • rows contains the contents of the CSV file and hence it is returned.
return rows
  • We iterate over each item of rows and write it to the CSV file using the methods provided by the csv package.
writer = csv.writer(temp_file)
from app.api.helpers.csv_jobs_util import export_orders_csv
content = export_orders_csv(orders)
for row in content:
   writer.writerow(row)

Obtaining the Orders CSV file:

Firstly, we have an API endpoint which starts the task on the server.

GET - /v1/events/{event_identifier}/export/orders/csv

Here, event_identifier is the unique ID of the event. This endpoint starts a celery task on the server to export the orders of the event as a CSV file. It returns the URL of the task to get the status of the export task. A sample response is as follows:

{
  "task_url": "/v1/tasks/b7ca7088-876e-4c29-a0ee-b8029a64849a"
}</span

The user can go to the above-returned URL and check the status of his/her Celery task. If the task completed successfully he/she will get the download URL. The endpoint to check the status of the task is:

and the corresponding response from the server –

{
  "result": {
    "download_url": "/v1/events/1/exports/http://localhost/static/media/exports/1/zip/OGpMM0w2RH/event1.zip"
  },
  "state": "SUCCESS"
}

The file can be downloaded from the aabove-mentionedURL.

References

Continue ReadingOpen Event Server – Export Orders as CSV File