Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
ferdiko committed Sep 6, 2023
0 parents commit 54debd8
Show file tree
Hide file tree
Showing 15 changed files with 2,707 additions and 0 deletions.
147 changes: 147 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Lab 0: Go tutorial

In this tutorial, we will briefly go over the most important concepts of the Go programming language. You will create a dashboard application visualizing data of the Massachusetts Bay Transportation Authority (MBTA). The main goal of the lab and the linked tutorials is to have you play around with Go --- familiarizing yourself with the covered concepts will come in handy for future labs! :)

## Installation

1. Follow [these instructions](https://go.dev/doc/install) to install Go.

2. We recommend using VisualStudio Code to work on the labs. VisualStudio Code can be downloaded [from here](https://code.visualstudio.com/download). After installation, make sure to add Go extensions and tools: Open VS Code, open the extension manager by pressing `Ctrl + Shift + x`, type "go" into the search bar and hit enter, find the Go extension by the GO team at Google and install it, open the command palette by pressing `Ctrl + Shift + p`, run `Go: Install/Update Tools`, select all tools and click ok.

3. Clone this github repository: In a directory of your choice, run `git clone https://github.com/MIT-DB-Class/lab0`.

4. [Download the database file](https://www.dropbox.com/s/37k6lrqd9uq52aa/mbta.sqlite?dl=1) `mbta.sqlite` and place it in the `lab0` directory (where `main.go` is).

5. You should be all set! You can run the code by typing `go run main.go` in your terminal from the `lab0` directory or clicking on "Run and Debug" in VS Code (on the very left, click on the icon with the bug and the triangle and then on "Run and Debug").

## Lab and Go walk through

In the following, we walk you through the lab and want to point your attention towards some Go features. After the installation, the structure of your project should look as follows:

```
.
├─ handlers/
| ├─ handlers_test.go
│ ├─ handlers.go
| └─ template.html
├─ ridership_db/
| ├─ csv_ridership_db.go
│ ├─ interface_ridership_db.go
| ├─ ridership_db_test.go
| └─ sqlite_ridership_db.go
├─ utils/
│ └─ render_chart.go
├─ main.go
├─ mbta.csv
├─ mbta.sqlite # TODO: download using link above
├─ go.mod
├─ go.sum
└─ README.md
```

### Hello world

In this lab you will implement a simple http server that visualizes ridership data from Boston's MBTA metro system. Go provides many tutorials with small sample applications that are a great way to play around with features of the language. For example, [this tutorial](https://go.dev/tour/basics/1) gives an introduction on Go's basic syntax, from structuring your files to writing a "Hello world" program.

Please use the linked tutorials and documentation to familiarize yourself with the Go language, which we will use for all labs in this course. The tutorials and documentation linked in this lab should allow you to fill out the missing functions required for the web app.

Please look at [this documentation](https://pkg.go.dev/net/http#hdr-Servers) for an overview on how http servers are created in Go.


### Error handling

Open `ridership_db/sqlite_ridership_db.go`. In line 14, we connect to a Sqlite database. This may fail because of various reasons, for example if the database file `mbta.sqlite` is not present. Go has a built-in `error` type which is used for error handling, and many methods in Go will return an error in addition to their normal return value. This is unlike the use of exceptions for error handling in Python and Java. [This tutorial](https://go.dev/doc/tutorial/handle-errors) discusses error handling in Go in more detail.


### Control flow
You've seen `if` statements when handling errors. `ridership_db/sqlite_ridership_db.go` also implements a very simple iteration logic. In line 33, we run a query against sqlite which returns `rows`, a pointer to the result set. In lines 40-47, we use the `next` and `scan` methods of `rows` to iterate through the result set and add the returned rows to a slice ("a list"). Both slices and pointers are explained in the next subsection --- for now, focus on familiarizing yourself with Go's control flow syntax.

Check out [this tutorial](https://go.dev/tour/flowcontrol/1) to make yourself familiar with control flow in Go.

### Pointers, structs, slices and maps

Now, go to `ridership_db/csv_ridership_db.go` and look at lines 10 to 15. You can see how we define the `CsvRidershipDB` struct (like we have previously defined the `SqliteRidershipDB` struct).

Note that when defining the struct, the asterisks ("\*") in lines 12 and 13 declare that `csvFile` is a *pointer* to an `os.File` and `csvReader` is a *pointer* to a `csv.Reader`. These pointer variables are used to store the memory addresses where the actual `os.File` and `csv.Reader` reside in memory.
The main use of pointers in Go is to "pass by reference" -- if a function takes a pointer argument and makes a change to the passed value, that value will also be seen by the caller (similarly if a function returns a pointer argument, the returned value may be changed by the caller). If the "\*" is omitted, this is "pass by value"; pass-by-value arguments to functions receive a copy of the value, and thus any changes made to them will be made on a copy and not seen by the caller.

In line 11, you can see how we define a *map*, which is Go's analog to a Python dictionary or a C++ unordered_map.
You will have to implement the `GetRidershipMethod` for `CsvRidershipDB`, which returns an int64 *slice* (`[]int64`) and an error. Slices are Go's analog to a Python list or a C++ vector. Note that slices and maps are always passed by reference in Go.

Look at [this tutorial](https://go.dev/tour/moretypes/1) to make yourself familiar with pointers, structs, slices and maps. If you want additional material on pointers, have a look at [this tutorial](https://www.geeksforgeeks.org/pointers-in-golang/?ref=lbp).


### Methods and interfaces

Both the `SqliteRidershipDB` type and the `CsvRidershipDB` type implement the `RidershipDB` interface, which is defined in `ridership_db/interface_ridership_db.go`. A type implements an interface by implementing all of its methods. Note that this is usually implicit -- nowhere do we explicitly indicate that the two ridership types implement `RidershipDB`. Interfaces allow us to simply use the `RidershipDB` type in lines 23-24 in `handlers.go`, without needing to distinguish which of the two implementations we are using (`SqliteRidershipDB` or `CsvRidershipDB`).

[This tutorial](https://go.dev/tour/methods/1) gives more detail on methods and interfaces.

### It doesn't stop here

The above resources provide a very brief overview of the most important features of the Go language. However, there are many more resources online as Go has a large and supportive community. We strongly encourage you to explore these resources and gain more experience with Go. If you find any tutorials particularly useful, please send them to us! :) 65830-staff [at] mit [dot] edu


## The Assignment

Complete the following two tasks for this lab:

### 1. Start an http server and handle requests

This task requires you to start up the http server in `main.go` and handle the user's GET requests by filling out the `HomeHandler` method in `handlers/handlers.go`.

The final web app looks like in the screenshot below, where users can select a T line (e.g. red line) and display its ridership statistics in a bar chart. The `HomeHandler` function first checks which line the user has selected in the drop down menu and then queries ridership numbers for that line from a `RiderhipDB` instance. The returned values are then displayed in a bar chart. You don't need to write code to plot the bar chart yourself, you can simply use the `GenerateBarChart` function in `utils/render_chart.go`.

After completing this task, you should be able to start the web server by running `go run main.go` and see the web app in your browser by going to http://localhost:PORT (where PORT is the port number you specified):

![Screenshot of web app](screenshot.png)

You should also be able to pass the test in `handlers_test.go`: When running `go test` from the `handlers` directory, you should get a similar output to this:

```
PASS
ok main/handlers 0.246s
```

### 2. Run a query over a CSV file

This task requires you to implement the missing methods in `ridership_db/csv_ridership_db.go`

Instead of issuing the query against sqlite, `CsvRidershipDB` directly runs it over the `mbta.csv` CSV file. MBTA divides a day into nine different time periods (*time_period_01*, ..., *time_period_09*). The CSV file contains how many passengers boarded trains during a specific time period, at a specific station and for a specific line and direction. For the queried line (passed to `GetRidership`) compute the total number of passengers that boarded a train for each given time period (for each time period, sum over all stations and directions). The sum for each time period should be an entry in the returned `int64` slice. Make sure to use the `idIdxMap` map to map the time period id strings (e.g. *time_period_01*) to the correct index in the `boardings` slice (e.g. 0).

To use your CSV implementation in the web app, instantiate RidershipDB to be a `CsvRidershipDB` instead of a `SqliteRidershipDB` in lines 23-24 in `handlers/handlers.go`:

```
// instantiate ridershipDB
// var db rdb.RidershipDB = &rdb.SqliteRidershipDB{} // Sqlite implementation
var db rdb.RidershipDB = &rdb.CsvRidershipDB{} // CSV implementation
```

You should also be able to pass the tests in `ridership_db/ridership_db_test.go`: When running `go test` from the `ridership_db` directory, you should get a similar output to this:

```
=== RUN TestRidershipDBsMatch
=== RUN TestRidershipDBsMatch/red
--- PASS: TestRidershipDBsMatch/red (0.00s)
=== RUN TestRidershipDBsMatch/green
--- PASS: TestRidershipDBsMatch/green (0.00s)
=== RUN TestRidershipDBsMatch/blue
--- PASS: TestRidershipDBsMatch/blue (0.00s)
=== RUN TestRidershipDBsMatch/orange
--- PASS: TestRidershipDBsMatch/orange (0.00s)
--- PASS: TestRidershipDBsMatch (0.01s)
PASS
ok main/ridership_db 0.226s
```

## Submission

50% of the grade is determined by an autograder, which checks if your code passes the test cases. The remaining 50% comes from us inspecting your code.

To submit your code, please do the following:

1. In the `lab0` directory, run `zip -r submission.zip handlers utils ridership_db main.go`.

2. Submit the generated zip to [the autograder](https://www.gradescope.com/courses/583077/assignments/3263816/).

3. The autograder should give you a score, based on whether your code passes the test cases. We will release the remaining 50% of the grade later.
11 changes: 11 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module main

go 1.20

require (
github.com/gorilla/mux v1.8.0
github.com/mattn/go-sqlite3 v1.14.17
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/wcharczuk/go-chart v2.0.1+incompatible // indirect
golang.org/x/image v0.11.0 // indirect
)
41 changes: 41 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/wcharczuk/go-chart v2.0.1+incompatible h1:0pz39ZAycJFF7ju/1mepnk26RLVLBCWz1STcD3doU0A=
github.com/wcharczuk/go-chart v2.0.1+incompatible/go.mod h1:PF5tmL4EIx/7Wf+hEkpCqYi5He4u90sw+0+6FhrryuE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo=
golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
62 changes: 62 additions & 0 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package handlers

import (
"encoding/base64"
"fmt"
"html/template"
rdb "main/ridership_db"
"main/utils"
"net/http"
"os"
"path/filepath"
"runtime"
)

func HomeHandler(w http.ResponseWriter, r *http.Request) {
// Get the selected chart from the query parameter
selectedChart := r.URL.Query().Get("line")
if selectedChart == "" {
selectedChart = "red"
}

// instantiate ridershipDB
var db rdb.RidershipDB = &rdb.SqliteRidershipDB{} // Sqlite implementation
// var db rdb.RidershipDB = &rdb.CsvRidershipDB{} // CSV implementation

// TODO: some code goes here
// Get the chart data from RidershipDB

// TODO: some code goes here
// Plot the bar chart using utils.GenerateBarChart. The function will return the bar chart
// as PNG byte slice. Convert the bytes to a base64 string, which is used to embed images in HTML.

// Get path to the HTML template for our web app
_, currentFilePath, _, _ := runtime.Caller(0)
templateFile := filepath.Join(filepath.Dir(currentFilePath), "template.html")

// Read and parse the HTML so we can use it as our web app template
html, err := os.ReadFile(templateFile)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
tmpl, err := template.New("line").Parse(string(html))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// TODO: some code goes here
// We now want to create a struct to hold the values we want to embed in the HTML
data := struct {
Image string
Chart string
}{
Image: "", // TODO: base64 string
Chart: selectedChart,
}

// TODO: some code goes here
// Use tmpl.Execute to generate the final HTML output and send it as a response
// to the client's request.
}
28 changes: 28 additions & 0 deletions handlers/handlers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package handlers

import (
"net/http"
"net/http/httptest"
"testing"
)

func TestHomeHandler(t *testing.T) {
// Create a mock server
srv := httptest.NewServer(http.HandlerFunc(HomeHandler))
defer srv.Close()

// Create an HTTP client
client := &http.Client{}

// Send a request to the mock server
resp, err := client.Get(srv.URL)
if err != nil {
t.Fatalf("Failed to send request: %s", err)
}
defer resp.Body.Close()

// Check the response
if resp.StatusCode != http.StatusOK {
t.Errorf("Expected status %d; got %d", http.StatusOK, resp.StatusCode)
}
}
24 changes: 24 additions & 0 deletions handlers/template.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<title>Lab 0</title>
<script>
function updateChart() {
var selectedChart = document.getElementById("chartSelect").value;
window.location.href = "/?line=" + selectedChart;
}
</script>
</head>
<body>
<form>
<select id="chartSelect" onchange="updateChart()">
<option value="red" {{if eq .Chart "red"}}selected{{end}}>Red line</option>
<option value="green" {{if eq .Chart "green"}}selected{{end}}>Green line</option>
<option value="blue" {{if eq .Chart "blue"}}selected{{end}}>Blue line</option>
<option value="orange" {{if eq .Chart "orange"}}selected{{end}}>Orange line</option>
</select>
</form>
<img src="data:image/png;base64,{{.Image}}" alt="Bar Chart">

</body>
</html>
13 changes: 13 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package main

import (
"fmt"
"main/handlers"
"net/http"
)

func main() {
// TODO: some code goes here
// Fill out the HomeHandler function in handlers/handlers.go which handles the user's GET request.
// Start an http server using http.ListenAndServe that handles requests using HomeHandler.
}
Loading

0 comments on commit 54debd8

Please sign in to comment.