In early January, I jumped head-first into software engineering by enrolling at Hackbright Academy, a coding bootcamp for women in San Francisco.

Hackbright’s mission is to empower female engineers and #changetheratio

After six weeks of learning skills in Python, JavaScript, SQL, and so much more, we’ve finally arrived at “project season,” the month-long section of the course during which we build our own apps from scratch. Every day, I’ll be posting my progress here (successes, failures, and all) to keep of what it was like to build my app. Though I’ve tinkered with smaller projects before, this will be my first substantial web app.

My app is called Book Bingo — it will be an interactive bingo board in which each square corresponds to a book genre. When a user reads a book that fits one of those genres, they can input the title and author into the appropriate square, and the app will connect to the Goodreads API to get more information about the book. When a user reads five books in a row, they get bingo!

For my MVP I hope to accomplish the following goals:

Player log in and log out functionality

Create one board on the backend, which all players will have access to

Display the board

Allow people to edit the board and input books

Connect to the GoodReads API

Once the MVP is complete, I hope to add functionality to let users to create new boards, and to allow multiple players to play against one another on the same board.

Day One: I jumped into my app by brainstorming my data model, and then building out my database using SQLAlchemy. As a break from database work, I built the skeleton of my Flask app, and implemented log in, log out, and registration functionality. I then created the basic model of a bingo board in HTML, and then finally went back to SQLAlchemy to modify the relationships in order to populate the database with test data. At the end of the day, the relationships weren’t quite perfect, but I’ll come back tomorrow with fresh eyes.

Day Two: Today’s first order of business was fixing my database relationships. Once that was sorted out, I was able to insert test data into my database. After realizing that hard-coding my board in HTML wasn’t the correct way to represent data, I scrapped the board I made yesterday, and got started making a new one within the database. Using SQLAlchemy and Jinja, I was able to get the genre names from the database and represent them as an HTML table (with a bit of tweaking, this will be the start of the board the user interacts with). Finally, I linked each user’s boards to their user page.

Day Three: Today I implemented functionality for a user to create a new board. Using an HTML form and SQLAlchemy, I was able to create a route where users can create their own board by choosing a board name and 24 genres to populate the cells.

Day Four: With the board finally completed, I spent the day working on an Ajax call which would allow users to type the name of their book into a text field, click “submit,” and see immediate changes on the DOM (for now, I just wanted the name of the book to appear in the relevant square). Though the Ajax call worked, I reached the first big bug of the project in trying to get the board to display feedback. Because I created the board through a Jinja for-loop, each square had the same HTML ID attribute, which made placing the feedback in the correct square difficult. I tried to solve this issue by setting the ID to be a Jinja variable, but jQuery wasn’t able to recognize Jinja, which meant I couldn’t get the feedback to work. I eventually solved the problem by setting a data attribute in HTML, setting it to equal a Jinja variable, and then using that data attribute in the Ajax call.

Day Five: With my Ajax call and board feedback finally working, I spent today creating the SQLAlchemy functionality to add new books to the database. When I was done with that, I started working on making board feedback permanent. I didn’t have much time to dive into that, so it will be my point of focus for the weekend and early next week.

Weekend #1: Over the weekend, I put together a series of SQLAlchemy queries to extract book title and author data from the database, and bring them over to my board through Jinja. I ran into an error in my database when I found a duplicate entry in my “BoardUser” table, but was worried about deleting it, in case it impacted the board it was connected to. I decided to put a hold on my project until I could talk to my advisor.

Day Six: After consulting with my advisor, I decided to scrap the SQLAlchemy queries I made over the weekend, and combine them into one giant query using joins. Once I successfully got the information I needed using the joined query, I set to work creating real-time changes to the DOM when a user enters a book into the database. Using Jinja, I created a conditional statement to check whether or not each square had an associated title. If it did, I assigned it a class called “read,” and used CSS to turn anything with that class green. I also went back to my Ajax call’s success function to, upon successfully receiving data for a specific square, use jQuery hide the content entry form for that square, and assign it the “read” class.

Day Seven: Today I connected my project to the Goodreads API. I set up the API calls using the “search by title” method to get the Goodreads ID, and then used that to get the description using the “show by ID” method. Now, when a user types a book title into a form, it returns the book’s description through Ajax. Though I’m not using them quite yet, eventually I plan to implement Bootstrap modals in each square, and at that point, I’ll place the book descriptions alongside each title. Now that my project is connected to the Goodreads API, I’ve reached my MVP!

Day Eight: Though I reached my MVP yesterday, I found several bugs last night: the first, and most important, is that my central SQLAlchemy query (which populates my board) is flawed — it only returns squares the user viewing board has populated, or which nobody has populated at all. Since, at this point, all the users share one board on the backend, this means that nobody but the first user can see every square. I was able to fix this issue by reworking my SQLAlchemy query with a subquery. The second issue was that on each user’s homepage, no matter which board link they clicked, they could only see the most recently created board. I noticed that this was an issue with Jinja, and fixed it accordingly. Finally, I wrote several unit tests.

Day Nine: For my MVP, my advisor wanted me to set up my project in such a way that multiple users could interact with the same board, but not see one another’s feedback. However, moving forward, I want users to be able to share a board, allowing them to play with/against friends. In order to let multiple users interact with the same board and see one another’s feedback, I had to reconstruct my central SQLAlchemy query (the same one I had fixed on day seven). Now, rather than joining together tables and querying for specific attributes, I set it up in such a way that there are five queries, one for each row, which takes all attributes from the Square table. Using relationships, I looped through the query results to create a list of five lists, and within each list I set up five dictionaries (one for each square, containing information about the square ID, genre, players attached to the square, and which books those players have read). Once the loop was working, I changed the Jinja loop which creates my board in order to use the lists of dictionaries. By the end of the day, the board working so that multiple users could play on the same board, and each user could see which books other users have read, while the board still displayed feedback based on what the user in the session has read.

Day Ten: Today I found a slight bug in my code — in the dictionary for each square, I have a “current_user” key, the value of which is True if the user who is logged in has read a book in that square, and False otherwise. However, because of the way the loop is set up, the attribute was only acknowledging the most recent user to read a book in that square. As a result, unless the current user was the most recent person to read a book in that square, the square lost the “read” class. In order to fix this, I changed the way I set up the current_user attribute, setting it by default to False and only changing it to True within the loop. Once I fixed the bug, I started considering the rest of the further features I’d like to pursue — namely, setting up alerts when a user gets bingo, and adding share links so players can invite friends to play on their board.

Weekend #2: This weekend I started working on setting up alerts when a player gets bingo. In order to do this, I used the x and y coordinate attributes of each square. I brought the coordinates over from the SQLAlchemy call which sets up the board, and set them as data attributes on the cell in HTML. I wrote a JavaScript function which checks for five squares with the same x-coordinate in a row upon form submission. To start off, I hard-coded it to check for squares with the Y-coordinate “1,” but plan to change it dynamically in the future. However, it seems that my JavaScript syntax is off, and I can’t seem to fix it without a second set of eyes. Going into week three, I hope to work out this syntax and to get the function working.

Day Eleven: After consulting with members of the Hackbright staff, I understood that my bingo alert function had several issues. First of all, the jQuery hasClass() syntax returns true if any item in a list of given items have a specific class, not all items. I rewrote my jQuery call to isolate items with both the read class and the relevant y-coordinate, and checked whether the list that 5 items long — if so, the alert function worked properly. Second of all, I was using $(“.submit”).on(“click”, getBingo) to call the function — however, I used the same syntax (with a different success function) to set up my Ajax call, so Javascript was never getting to the bingo alert function. Instead, I decided to call getBingo() inside the Ajax success function, and got it to work! Next came the issue of generating the bingo alert dynamically, rather than hard-coding it. I had trouble isolating the X and Y coordinates from the bingo board, in relation to the specific square in which data was being submitted. I had to learn more about “this,” parents, and siblings, and was eventually able to bring the coordinates into my Ajax call. Once that worked properly, I was able to rewrite the function to dynamically check for bingo. Finally, I wrote alternate if-statements to check for bingo diagonally across the board.

Day Twelve: This morning, I realized that there was a bug in my code for my diagonal bingo alerts — once all of the necessary squares had the “read” class, every single time a new book was entered anywhere on the board, the “Bingo!” alert came up. After looking into it, I realized this was because of the way I’d written the logic — once those squares were “read,” the logic for bingo always remained true, even if the square you were currently dealing with wasn’t one of them. In order to fix this, I had to refactor the code to deal specifically with the X and Y coordinates coming in with that specific square. Once it was fixed, I created a new route for a share link based on the board ID. While looking at a board, users can copy the relevant sharing URL and send it to a friend who — upon clicking on it — will have the board ID in their session. After registering or logging in, that user gets added to the board in the database, and the site redirects to the appropriate board. After the share links were working, I refactored my server file to prepare for writing unit tests.

Day Thirteen: Because my project is currently in a good place in terms of functionality, I decided to take the day to write unit tests. I incorporated the unit tests I’d previously written into a new file, and then wrote additional tests. By the end of the day, my project was at 80% coverage.

Day Fourteen: This morning, I explored Jasmine testing for JavaScript. However, because none of my JavaScript functions return anything explicitly (rather, they change the DOM), I realized it wouldn’t be an effective testing method for me. After deciding to table JavaScript unit testing for another day, I implemented Bootstrap modals into my project. Until this point, when a user read a book, the information about that user and that book would simply append to a div below the form in each square — however, this was extremely inelegant. Moreover, I’m getting book description information from the Goodreads API, and if I want to use it for each book, I need a better way to display everything. By putting a modal inside of each square, I’m able to have a fresh canvas on which to display that information without muddying up the board. I ran into some trouble getting the modal buttons to work for each individual square, but by the end of the day, I had working modals!

Day Fifteen: Now that I have working modals, I want to finally bring the book descriptions that I’m getting from the Goodreads API over to the board, so I can display them (the idea being, users can see the descriptions of the books their friends have read). I added API calls to the SQLAlchemy query that gathers information about each book, and quickly realized that this method of getting information completely ruined my runtime efficiency. In order to fix the issue, I had to modify my database’s Books table to add a “Description” column (along with a Goodreads URL column, while I was modifying it). I dropped my database, and added the necessary API calls in the function that adds books to the database. Unfortunately, due to Hackbright’s Friday schedule, this was all I had time for this today. Over the weekend, I hope to test it out and make sure I’m getting the correct information in and out of the database.

Weekend #3: Over the weekend, I added API calls to the function that adds books to the database. However, I noticed that as a side effect of the Goodreads API returning XML, HTML tags such as “<br>” and “<b>” were showing up as part of the description strings, and not rendering as HTML on the front end. In order to solve this, I used regular expressions to strip anything between those tags out of the string.

Day Sixteen: This morning, the first thing on my to-do list was refactoring my code for diagonal bingo alerts to make it less repetitive. Once that was done, I set about finding a way to make my “bingo” alerts less obtrusive. I found a jQuery plugin called jQuery-Confirm which makes alerts look nicer, and implemented it. Finally, I started adding more Bootstrap elements, such as a jumbotron on my homepage, and customization of my modal buttons.

Day Seventeen: In order to get a bit more front-end experience, I decided to add data visualization to each board page to display how many books each user on the board has read. The SQLAlchemy query required to get the desired information was trickier than I anticipated, so I spent quite a bit of time fine-tuning it. When my query was finally returning the results I wanted (a list of tuples with each player’s name and how many books they’ve read on that board), I started looking into several data visualization plugins, including Chart.js and Plotly. After reading the documentation for both, I decided Plotly was the better choice for my project, and I implemented a “Board Stats” graph on each page.

Day Eighteen: Because I felt like I was in a good place in terms of my progress on my project, today I took the day off to review concepts in computer science, such as graphs, binary search trees, and linked lists. We’ve been learning about algorithms and data structures while working on our projects, but since my project takes up so much of my concentration, I haven’t had as much time to review them as I’d hoped. Taking the day to practice data structures was a great way to reinforce the concepts I’ve been learning.

Day Nineteen: Today I dove head-first into Selenium testing. There were a number of obstacles to overcome before I could get Selenium to work: first of all, Selenium does not work on the Hackbright virtual machine, so I had to switch to my local terminal. Next, I learned that Selenium is only compatible with Firefox 47.0.1, so I had to downgrade my Firefox accordingly. Finally, I had to install the GeckoDriver into the appropriate file path. By the time all was said and done, I only had time to implement a few Selenium tests, but I was still excited to see them work — watching the Selenium WebDriver operate on it’s own is extremely satisfying!

Day Twenty: Due to Hackbright’s class schedule, I didn’t have much time to work on my project today. With the time I did have, I cleaned up some logic in my server file, such as checking the user’s session in order to make sure they can only access their own user page and boards. At the end of the day, code freeze went into effect — from this point forward, I’m only allowed to change design-related aspects of my code.

Weekend #4: With my functionality done, I took the weekend to review topics in computer science, and to start brainstorming design ideas. I implemented a few Bootstrap features, and looked at design inspiration around the internet.

Day Twenty-One: Today was the first of two days allowed to “prettify” our apps. The first thing I did was pick out a color scheme — I knew I wanted to incorporate maroon and bright green, and chose other colors based on that. Next, I formatted the user homepage. I initially designed a book-shaped vector graphic and planned to lay out the user’s name on one side, and boards on the other, but after implementing the idea, I decided I didn’t like it — particularly because it was hard to scale the graphic with Bootstrap’s responsive features. After scrapping that idea, I settled on a simple green and white background with an image of a book on it. I searched around Hackbright until I found a book that wasn’t about Python, took a picture, and used Adobe Illustrator to stick it onto my background image. With that design in place, I used a similar green-and-white background with a picture of a book for the New Board page. The most difficult aspect of the design was trying to manipulate CSS to get my background images to scale nicely with the browser window. Finally, I changed fonts and colors on the bingo board page in order to bring everything together.

Day Twenty-Two: Today is the last day to work on our apps — tomorrow is demo night! Though today was ostensibly the second day for “prettifying” our apps, the truth of the matter was that most of the day went into creating my screencast for Demo Night, when we present our apps to partner companies. I recorded a screen capture video with RecordIt, and then edited it to match up with my planned speech using Adobe Premiere. By the end of the day, my app and screencast were ready to go!

There you have it: the step-by-step process of how I built my first substantial app over the course of a month. Though it was difficult at times, it was an amazing learning experience, and I can’t wait to dive into my next project.

Book Bingo homepage — check out the link to see more!

Want to see the final product? Visit: http://book-bingo.herokuapp.com/, or check out the code at: https://github.com/jessapp/book-bingo

Jessica Appelbaum is a software engineer living in San Francisco, California. Connect with her here!