Stop throwing your data into file cabinets! Share it online, instead.

It is time to enter the modern era. Cast off the shackles of FORTRAN, don your Pythonic armor, and forge ahead! It is the age of sharing your data with not only your colleagues, but also the interested individual. The tools are available and easy to use, and I want to show you how to do it.

In this article you will learn how you can:

  • Build a website visually using the Anvil tool (entirely in Python)
  • Display your data beautifully and simply with the Plotly module
  • Share it with the world

Go put on a pot of coffee, it’ll help. ??(I’ve converted to instant for convenience’s sake. I know I’m a sinner.) Let’s get started!


Anvil ?is a?tool that allows you to build modern web applications easily and entirely in Python. It was co-founded by Meredydd Luff, featured in the tweet above. I like this tweet because it really shows you how excited he is about building a great tool and making it easy to use. This is totally reflected in the quality of the?documentation ?and design of the editor. OK, enough gushing, how do you actually use it?

Building a web application visually in the Anvil Editor

Let’s head on over to?anvil.works .

No alt text provided for this image

Click on “Start building” in the top right-hand corner. This should take you to the login page:

No alt text provided for this image

Sign up for an account, or log in if you already have one. Once you have logged in, you should see the Anvil Editor landing page.

No alt text provided for this image

Your site might look a bit different because I have already created a few applications.

Woohoo! We are poised to create our first app on Anvil. First, let’s make a plan. What are the requirements for our app?

Thinking about the main goals:

  • It should serve our end user plots of our data
  • It should allow the user to explore the data with some degree of customization

From a user experience perspective:

  • It should be fast enough to keep the user’s attention
  • It should feel intuitive and easy to use

And finally, I know that your time is limited and valuable:

  • It should not take months to develop

Take a sip of the coffee you made earlier…it’s always nice to set out with a plan in mind. These are some pretty lofty goals, but they will help guide the decisions we make about how and where to spend our time building the app. Before I start, a disclaimer: The Anvil docs are far better at explaining all of this in detail; I want to get you up and running as fast as possible. If you want to learn a bit more, please do read the docs!

Designing the app

I will be the first to mention my creative ineptitude, and my clothing choices telegraph that. Anvil provides such an easy toolset that even?I?can design beautiful, modern-looking web applications. Let’s go back to the Anvil editor landing page and click on New Blank App.

No alt text provided for this image

I will be using the Material Design template because I think it is the nicest looking and most intuitive to use on all devices. I highly recommend that you follow along with me. If you want a bit of an extra challenge, try choosing another template and see how different our final products end up.

Once you have selected the template that you want to use, you will be dropped into the Anvil editor. Don’t worry if it looks intimidating, we are going to spend some time dissecting it. If you want to dive a bit deeper,?here is the appropriate section in the docs .

No alt text provided for this image

Let’s start from the left and move right.

No alt text provided for this image

This is the App Browser. You can toggle its visibility with the button in the top left corner. From here, you can access the different forms that you have created, add services to your app, or change the style of your app. We will mostly be concerned with our homepage, Form1, and our server code below it.

No alt text provided for this image

This is the Form Editor. It has two views: Design and Code. In Design view, we can drag and drop components from the Toolbox and build our app visually. Then, in the Code view, we can add functionality to the components. We will make the Code view and Design view work together to dynamically plot our data at the click of a button.

No alt text provided for this image

This is the Toolbox. You can access all the elements you might want to use to build your app. Simply drag one of the components into the Design view in the Form Editor and we are in business! Go on, try it out. Just throw any old component in there, you can always undo it or delete it. You cannot break this thing. Once you are done goofing around, drag a Label component from the Toolbox and drop it in the upper left corner.

No alt text provided for this image

After you drop the Label component, its properties panel will appear. We are just going to change the value in the text property box. Give your app a descriptive name, something that helps your end user understand what to expect. I am going to be using the?Nuclear Data ?dataset from Kaggle, so I’ll call my app “Properties of Nuclei.”

Now that we know the name of our app, we should probably rename our project inside the Anvil Editor. Click on “Material Design 1”, next to the Run button. A dialog box will appear allowing you to rename your app.

No alt text provided for this image

Confirm the change and we are set. Now let’s add one of the key components of our app: a Plot component. Drag a Plot component into the middle of our Form Editor. It should look something like this:

No alt text provided for this image

At this point we just have an empty plot. Let’s add some DropDown components to indicate what features we want on each axis. This is an option that doesn’t need to be visible all the time; especially on small screens. We can tuck it away in the sidebar so that it will be visible with enough screen real estate, and automatically hide away if we don’t have the room. We should start with a ColumnPanel component to provide a bit of structure for the other components that we add in.

No alt text provided for this image

Next we can add our controls. We will add two DropDown components: one for the x-axis and one for the y-axis. For an added challenge you could try adding more complicated functionality, i.e. a way to select a subset of your data. Try this after seeing how components in the Design view are linked to the functions in the Code view. First, let’s drag two Label components into the ColumnPanel and edit their text to read, “x-axis”, and, “y-axis.”

No alt text provided for this image

Next, add a DropDown component beneath each label. Change the name of each DropDown component to “x_axis”, and “y_axis”, respectively.

No alt text provided for this image

Finally, we can add a “Plot!” button to create the plot. Drag a Button component below the y-axis DropDown component. Change its text value to “Plot!”. The default button doesn’t really look that clickable, so change its role value to “primary-color.” You can play around with the different options to see what you like.

No alt text provided for this image

We are just about ready to jump into the code, but first we need to get a dataset and upload it to Anvil.

Retrieving the dataset, and throwing it at Anvil.

I’m going to try to stay a bit loose here. The point of this article is not to show you how to make a dashboard for a specific dataset; I want to show you how you might make your own dashboard with your own data. With that in mind, I’ll pick a specific dataset and you can follow along with that one. For an added challenge you can try to follow along with your own data. You might want it to be in a CSV file format with numerical column values and a header row with column names for one-to-one correspondence with my code. If you have your data in a different format, go check out the?Anvil Docs ?for guidance.

I am using the?Kaggle Nuclear Data ?dataset, like I mentioned earlier. Download the dataset from Kaggle and extract the archive file somewhere. There should be a CSV file called “all_nuclei.csv”; it looks like this.

No alt text provided for this image

This fits our required format: a header row of column names, and numerical values only. There are some NA values that will cause some issues later, but we have a solution, as well.

We need to somehow make this information available to Anvil. There are a number of options for this task, but the simplest is to upload our csv file to a Data Table inside of our Anvil app. You can?read the docs here . To start off, we need to add the Data Tables service to our app.

In the App Browser panel, under the Services section, click on the plus symbol to add a new service. A dialog with all the available services will pop up. Select the Data Tables service.

No alt text provided for this image

In the Data Tables tab, we must perform a couple of steps to prepare our Data Table. First, let’s add a new table. Click on Add a table > New Table.

No alt text provided for this image

We should rename our table to something a bit more descriptive. To rename a table just click on its name, in this case “Table 0”. If you are following along with the Nuclear Data CSV file, I’ll name it “all_nuclei” because that is what the file is called. The name of our data table corresponds with the name we will use to access the table from within our Python code. We will see how this works in just a bit.

No alt text provided for this image

We will want to access this data table from our Client Code, so let’s change the permissions to allow that. Click on the drop-down menu next to “Permissions: Forms:” and select Can Search Table with the eye icon.

The last thing to do is check the box that says, “Auto-create missing columns when adding rows.” This will allow us to upload any CSV file and it will automagically create new columns based on the names in the header row.

Now that our table is prepared, we need to create a script on our computer — in Python — that will upload the CSV file to “all_nuclei”. To do this, Anvil provides the Uplink. The Uplink is a service that connects a python script on your computer to your app. We will use it once, to upload our CSV file, but it allows you to host functionality that is not provided with the free Anvil service securely on your computer. Anvil does this intentionally so as to not limit what developers can do with a small budget. If you are interested in using Anvil for larger project with more requests, or you don’t want to run a script on your local computer all the time, consider paying for an?Anvil plan !

Preparing the Uplink

We need to do a couple of things before we can use the Uplink to upload data to our data table. In our project we need to enable the Uplink. We can do this from the settings button.

No alt text provided for this image

Once you have clicked it, select “Uplink…”

No alt text provided for this image

and click on “Enable the Anvil Server Uplink for this app.” You will be presented with an uplink key which is used to connect to this app from your local Python script. Follow the other instructions on your local machine:

In a terminal, run

pip install anvil-uplink        

We are also going to need pandas if you don’t have it

pip install pandas        

Open a script in the same directory as your CSV file. Name it, “upload_data.py” and add the following code, from the?Anvil docs

import pandas as pd 
import anvil.server
from anvil.tables import app_tables
anvil.server.connect("{insert uplink key here}")
def import_csv_data(file):
  with open(file, "r") as f:
    df = pd.read_csv(f) 
    df = df.dropna()
    for d in df.to_dict(orient="records"):
      # d is now a dict of {columnname -> value} for this row
      # We use Python's **kwargs syntax to pass the whole dict as
      # keyword arguments
      app_tables.all_nuclei.add_row(**d)
import_csv_data("all_nuclei.csv")        

Let’s walk through this, step-by-step

import pandas as pd 
import anvil.server
from anvil.tables import app_tables        

We first import pandas for the very convenient DataFrame type. A big reason for us using the Uplink method here is that the free Anvil plan does not provide access to pandas. We also need to communicate with the?anvil server ?and access the?app’s data tables .

anvil.server.connect("{insert uplink key here}")        

This line establishes a connection with our app. Don’t forget to change the placeholder text with your app’s uplink key!

def import_csv_data(file):
  with open(file, "r") as f:
    df = pd.read_csv(f) 
    df = df.dropna()
    for d in df.to_dict(orient="records"):
      # d is now a dict of {columnname -> value} for this row
      # We use Python's **kwargs syntax to pass the whole dict as
      # keyword arguments
      app_tables.all_nuclei.add_row(**d)        

We define the?import_csv_data?function. It takes as a string, the filename of your CSV file, and adds each row to our app’s data table.

I will highlight one line here:?df = df.dropna()?. This is how we are dealing with the NA values we saw earlier. This drops all rows that contain NA values. If your project requires that you keep rows containing NA values, then you will have to think about how to modify this section. The problem is that Anvil data tables will not accept NA values, so you will likely have to change them to valid numerical values.
with open(file, "r") as f:        

This creates a file object that can be read by?read_csv

   df = pd.read_csv(f) 
   df = df.dropna()        

df?is now a pandas DataFrame without any NA values.

for d in df.to_dict(orient="records"):        

As the comment describes,?d?will be a dictionary of?key:value?pairs where the keys are your column names, and the values are the particular values for that row.

app_tables.all_nuclei.add_row(**d)        

This is where the magic happens. The?app_tables?module provides us access to whatever app we connected to with?anvil.server.connect(). We are accessing the?all_nuclei?table — remember earlier when we saw the Python name of our data table? — and adding a row. This should be fairly copy-pasteable, but it is good to understand what is going on. This is especially true if you want to modify this project with another CSV file, for instance.

import_csv_data("all_nuclei.csv")        

The selection of your CSV file happens here, in the last line of our script. I have “all_nuclei.py” in the same directory as this script. You should make sure that your CSV file is, too.

Hop into a terminal and navigate to the same directory as your script and CSV file and run your script

python3 upload_data.py        

If it runs without errors — and if there are errors, please comment — then you can check your app’s data table and see if it has entries.

No alt text provided for this image

Viola! We have data! You must be on your second cup of coffee by now?

Adding functionality through the code

We are finally ready to jump into the code. I hope you are all still with me! Let’s go back to Form1 in our Client Code section and finally take a look at the Code tab.

No alt text provided for this image

Anvil provides a stub method for our Form including an?__init__?function. If there’s anything that we want to have happen as soon as our app loads, this is where we should do it. We do want to populate the DropDown components with the column names from our?all_nuclei?data table as soon as our app loads. Inside the?__init__?function, add the following

# app_tables.all_nuclei.list_columns() returns a list of dictionaries like {'name':name, 'type':type}, we just want the names.
columns=[entry['name'] for entry in app_tables.all_nuclei.list_columns()]
self.x_axis.items = columns
self.y_axis.items = columns        

If you run your app now(press the Run button up top) you should see the DropDown components. If you don’t, try pressing the hamburger icon in the top-left corner. Your DropDowns should be populated with the column names from your data table. If you used your own CSV file with different data, you should see your column names here.

No alt text provided for this image

Woohoo! We are trucking along. Next up, let’s add functionality to the “Plot!” button and actually make a plot appear! Go back to the Design tab in Form1. Click on the “Plot!” button and scroll to the bottom of its properties. We are looking for the Events section and the click event. Click on the two arrows next to the click event.

No alt text provided for this image

After clicking this button, a couple of things will happen. First, Anvil will generate a stub function named?button_1_click(). Then, it will link it to the “Plot!” button so it runs when “Plot!” gets clicked.

No alt text provided for this image

When the “Plot!” button is clicked, we want to run a server-side function to create the plot, then send it back whenever it is done. “Why not just write it here?”, you might ask. Plotting is a long-running and intensive process, and the client code runs in the user’s browser. We want to limit the amount of computation being done on the client side. This means making sure that we aren’t consuming tons of their RAM or making the page unresponsive. With this stub function created, let’s write the server side code to generate a plot object the we can pass back to our client code.

Plotting the data server-side

Let’s start by opening the Server Code module.

No alt text provided for this image

Click on Add a new Server Module.

No alt text provided for this image

First, we need to import the?time?module so that we can log our server response time.

import time        

In the server module we can define functions that are callable from our client code, but actually run on Anvil’s servers. We do this by adding the [email protected]?to the line above our function. Add the following function to your server module.

@anvil.server.callable
def make_plot(x_name, y_name, n_rows): 
  start_time = time.time()
  data = app_tables.all_nuclei.search()
  x_data = [row[x_name] for row in data[:n_rows]]
  y_data = [row[y_name] for row in data[:n_rows]]
  print(f"It took {time.time()-start_time} seconds to retrieve {n_rows} of the data.")
  return (x_data, y_data)        

This function loads the data from the?all_nuclei?data table and returns?n_rows?of only the columns that were chosen by the user.

We cannot load the entire dataset because Anvil limits the amount of time a server module can run. I recommend playing with?n_rows until you find a number that doesn’t timeout the server.

Now, back in the client code, we can fill out the?button_1_click?stub. Replace the?pass?keyword with the following.

x_data, y_data = anvil.server.call('make_plot'
                                       , self.x_axis.selected_value
                                       , self.y_axis.selected_value
                                       , 10000)
plot_data = go.Scatter(x=x_data, y=y_data, mode='markers')
self.plot_1.data = plot_data        

We first retrieve our x and y data from the server via the?anvil.server.call()?method. The we build up a scatter plot using Plotly’s?graph_objects?module(we made the alias?go?when we imported it). Finally, we set the data for our applications plot to be the Scatter object that we just made.

You can read more about server modules in the?Anvil docs , and about Plotly’s?graph_object?module at the?Plotly docs .

If you run your app now, we should have the ability to actually create a plot! You will see some output after pressing “Plot!” and the plot should appear with the chosen axes.

No alt text provided for this image

Yay! Now all that is left is to spruce it up a bit with some labels. Add the following lines into the?button_1_clickfunction in the client module.

self.plot_1.layout.title = f"{self.y_axis.selected_value} vs. {self.x_axis.selected_value}"
self.plot_1.layout.xaxis.title = f"{self.x_axis.selected_value}"
self.plot_1.layout.yaxis.title = f"{self.y_axis.selected_value}"        

The final client module looks like this.

No alt text provided for this image

The final server module looks like this.

No alt text provided for this image

And if we run our app and plot some features.

No alt text provided for this image

Our app is fully developed and we are now ready to share it with your colleagues and the world! For now, make sure to set the?n_rows?variable to something that doesn’t take forever to load(or worse, timeout the server) and still tells the story behind your data. I would start with 5000 and play around while watching how long it takes to load in the Output tab.

Publishing your app

Once you are happy with the look and loading times of your plots, it’s time to publish. Run your app from the editor, as usual, and press “Publish this app” in the top right-hand corner.

No alt text provided for this image

Click on Share via public link.

No alt text provided for this image

You will be given a random name, but you may choose your own. Not all names are available. If you choose a descriptive, memorable one, then it is more likely for people to remember to visit your app! I chose?nuclear-properties.anvil.app ?as the name for my app. You can even visit my app and compare the functionality with your own app. Once you hit Apply, you can share your link with all your colleagues, your neighbor, the milkman, maybe even your grandmother! I highly recommend that you check out the?Anvil docs ?for some ideas about how to extend this project and add new functionality. Here are a few suggestions to get you started:

  • We often want to visualize more than two variables to understand the richer features, try adding a second or third plot. Look into the?RepeatingPanel ?to add plots on-demand.
  • Add the ability to cut on a feature of your data to visualize a subset of your data.
  • Investigate the parameters of?go.Scatter?and customize your plot to include extra data on hover.
  • Look at the?Plotly docs ?to get inspired about other ways to visualize your data. I’d check out?go.Bar?for histograms.

I was also kind of sloppy about my programming practices throughout this article and I would like to address that. While I heartily — and in some crowds obnoxiously — endorse better programming practices, this article is for the individual who has *some data* and wants to share it as quickly and easily as possible. I also tried to write this article in an organic way. As I wrote the article, I did the tasks in step. The next step, for me, would be refactoring and reorganizing (before I add more features and start the process again). Here are some small refactors that I noted along the way:

  • The homepage for our app is called “Form1”. We should give a more descriptive name like “SimplePlotForm”, or “Homepage”. The latter being less good.
  • Each component is given a name that you can use to refer to it from within your python code. Right now we have things like?button_1?for out “Plot!” button. We can rename them just like we did with the two DropDown components by clicking on the component in Design View and clicking on the name property near the top of the Properties Panel.

Thank you for making it all the way to the end! I hope that this article helps you to more effectively communicate the story behind your data in an easy-to-use and interactive way. If you have any troubles, please reach out to me in the comments below or on twitter?@SteffanicCodes . Check out my?GitHub ?for some of my other projects!

If you build a really cool Anvil app, tweet it @ me! Happy Building!

Cheers!



要查看或添加评论,请登录

社区洞察

其他会员也浏览了