Stop throwing your data into file cabinets! Share it online, instead.
Patrick Steffanic, Ph.D.
Early career data scientist and recent graduate. Relentlessly positive and excited to learn and grow.
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:
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 .
Click on “Start building” in the top right-hand corner. This should take you to the login page:
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.
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:
From a user experience perspective:
And finally, I know that your time is limited and valuable:
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.
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 .
Let’s start from the left and move right.
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.
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.
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.
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.
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:
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.
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.”
Next, add a DropDown component beneath each label. Change the name of each DropDown component to “x_axis”, and “y_axis”, respectively.
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.
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.
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.
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.
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.
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.
Once you have clicked it, select “Uplink…”
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.
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.
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.
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.
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.
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.
Click on Add a new Server Module.
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.
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.
The final server module looks like this.
And if we run our app and plot some features.
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.
Click on Share via public link.
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:
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:
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!