LMS for Musicians: React Redux

Read more about the article LMS for Musicians: React Redux
Photo by Pixabay on Pexels.com

If the COVID 19 pandemic has taught us anything, it is that virtually everything can be virtualized. Zoom lessons have become a feature of life for students. Many teachers have taken the opportunity to collect and create resources for their students through a variety of platforms. I decided to design my React Redux project to serve this demand for virtual music education resources.

This current version of my project is only “music” related in name. I hope to incorporate many features specific to music education as I continue development and roll out some live versions for friends and colleagues, but this portfolio project represents more of a vanilla LMS. Users can sign up or log in, subscribe to courses, and record their progress through the course.

The backend models for the project started simply enough with Teachers having a collection of Courses which in turn are connected to a collection of Lessons. To allow for Users to have a collection of Courses, I created a Subscription model which serves as a join table for one User and one Course. This may seem unnecessary since Users could simply have_many Courses, but I knew from the start that keeping track of a User’s progress through a course would be best stored in the actual instance of this connection.

I ended up creating a final model called LessonProgress to store this information. Every Subscription has_many LessonProgresses, and a collection of LessonProgresses is generated automatically after a Subscription is instantiated. For simplicity’s sake, a LessonProgress stores a Lesson’s Id and a “status” as a string. I chose to use a string instead of a bool to allow for future implementation of multi-state LessonProgresses, which could include “pending review”, a “failed” state of an assessment, or a status to indicate that a lesson was started but has not been completed.

The utility of an LMS depends on having a secure login for users to access their courses and progress through the content at their leisure. Therefore I turned my attention first to the task of creating a sign up and log in process. This involved a simple form component in the frontend to take the username and password for a new or returning user (To scale the project, I will likely replace this simple username structure with full name and email for communication integration and security). After validating and creating a Session, the User object is sent by the API to the frontend.

This is where I believe the Rails API will end up falling flat when I decide to take this project to development. A more robust session creation process using cookies and full scale Rails routing will likely be the first in a list of improvements to the app as I continue to scale towards production.

To keep my Redux store simple, I needed most of my data to be structured into a single user key. To accomplish this, I needed to be sure to include all of the associations within the rendered User. To do this, I created serializers for each model in my database, making sure to include the associations I needed in this file. In addition, I altered the config/initializers/active_model_serializer.rb file to read as follows:

ActiveModelSerializers.config.default_includes = ‘**’

This allows my serializers to automatically include up to two levels of associations when rendering, giving me the depth of association needed to include Users that have many Subscriptions with many LessonProgresses and one Course.

From here, I created a spiderweb of containers and components to render (depending on the user property of the store) the correct courses and lessons. Further flow control and a new set of actions allowed me to update the backend’s LessonStatus within a subscription and allow a user to progress through a course one lesson at a time.

I initially set up the app to show the courses of multiple teachers. I realized this use case was very limited for real world application, so I pivoted the application to render a single teacher’s work. I am thankful to my friend at Horn Hippie Media for allowing me to use his existing YouTube content to serve as seeds for this project, in addition to his fantastic branding!

A quick demonstration of the UI of the Music LMS

I have to say, designing a frontend user experience has been very satisfying with React. I have learned many ins and outs of styling buttons and Router NavLinks, and I enjoyed designing a simple container style to go with the bright and bold colors from the Horn Hippie Media branding. Further improvements to the responsive styling of the navigation components will definitely come with further development, in addition to some render animations to make the user’s experience even more smooth and seamless. My goals for this project also include CReate, Update, and DElete (CRUD) for teachers, allowing production versions of the application to be “self service”. Incorporating payment and designing a paywall for the content on the app will also be essential to a production version of the app.

Overall the process of designing and building with React has been incredibly satisfying. The structure of passing properties and state between components clicks well with how I visualize the structure of the application, and creating flow controls based on the central store of information made the addition of features simple to add to the app.


Read more about the article Lemonade
Photo by Julia Zolotova on Pexels.com

My final project for the Flatiron School has been brought on a cocktail of mixed feelings. When I started the web development course at Flatiron in the summer of 2020, so much was uncertain. How would the performing arts fare in the 2020 pandemic? Would my job in the orchestra still be there when the dust settled? Most importantly, what was I going to DO with myself in the meantime?

As I near the end of the bootcamp, I am thankful that the Houston Symphony made lemons out of lemonade. We are very fortunate to be maintaining both our jobs and a regular performance schedule. I am also very grateful to the Flatiron School for injecting purpose and direction into a potentially stagnant and empty year.

All that being said, I do find myself at a real crossroads in my coding career. Flatiron seems to be designed to take newbie coders all the way from “zero to hero”. The tail end of the Flatiron experience includes consultations with a career coach (Hi Shelley!) designed to prepare you for the audition interview process and to enter the professional world as a newly minted developer. I am lucky to have this resource as I navigate how to incorporate my new skills into my life as a full time musician. How can I best use my new skills? What role will coding have in my life when the concert season is back in full swing?

I find myself utilizing some skills and approaches from learning to code to my career goals and music to guide me through these uncertainties, to continue moving forward, and continue to make lemonade.

Keep the end result in mind.

With any coding project, and in life in general, it is easy to get distracted and lose sight of your overall goal. Set clearly defined and achievable goals for short and long term periods.

Short term: add a subscribe button to each course of the LMS app. Write a blog post, and connect with potential employers on LinkedIn.

Long term: Create a structured user experience to guide users through a course, recording lesson progress to the backend. Score a job and start building solutions to real world problems.

Focus on incremental change.

It may seem counter to keeping long term goals in mind, but this skill has to work hand in hand with creating goals to effect a growth mentality. As you remain mindful of your short and long term goals, you can concentrate your daily efforts on inching your way towards accomplishing those goals. My mantra through my time at the Flatiron School has been “a little bit every day”.

Some days I feel like I have burned through an entire section of material, and everything clicks. Progress is swift and satisfying, and I am energized to dig in the next day. Some days, on the other hand, I remain stuck on a single small problem, or struggle to internalize a new concept. Whether I move myself one mile closer to my goals or one inch, I remind myself that progress of any kind is the goal. Looking back, I am most proud of those frustratingly slow days, where I was triumphant over a particularly difficult part of the path to success.

Good => Great

As a musician, we are trained to build our skills to deliver compelling performances with precision and consistency. However, one cannot achieve this level of performance without building skills through practice. No matter how talented a musician is, “greatness” never comes before “goodness”. The same goes for building an application! Any project is a constant evolution, starting by achieving some very basic levels of data handling and user experience and being refined to the end goal as a meaningful solution to a real problem. As I look back, realizing the incredible journey from hello world to an interactive learning management system reminds me to embrace and celebrate the “good” that comes on the way to “great”.

These are my guiding lights as I move towards graduation: know where you want to go, commit to making one step at a time, and enjoy the journey. I hope these concepts will help you proceed into uncertainty in your own life, thanks for reading!

RSVP – using AJAX for simple reservations

Read more about the article RSVP – using AJAX for simple reservations
Photo by John-Mark Smith on Pexels.com

One of the historical problems facing Third Space Music was collecting reservations for their concerts. There was no admission charge, but the concerts had a limited accommodation.

I became the webmaster for Third Space Music during the 2020 pandemic. After going through a complete rebrand, our group launched a brand new website design. All of the concerts presented this season have been livestreamed to our audience and we will hold our first ever ticketed livestream event in February 2021. A simple woocommerce web store serves as the host for our ticket sales for this event.

The problem remains: how will we administer an RSVP service when our concerts return to a live setting? How can we collect emails and names for our patrons, and limit the number who receive details and access to the concert when we have limited capacity?

I decided to create a solution to this as my frontend project for the Flatiron School.

Stubbing out the Backend

After generating a rails API app structure and creating a separate backend and frontend folder, I set about generating migrations for my backend classes. For my MVP (minimum viable product), I needed Events with a collection of Users, but I also needed Users to be able to belong to many events. I settled on a join table class called Reservations that would belong to one Event and one User. To future-proof my app, I added an Admin class. Each Event must belong to an Admin, if I take this app live or build it up in the future this will be an important system to keep basic users from altering the RSVP information and guest list. For now, a simple representation of the connection between the front and back end of my app should do! I checked my migrations, made sure to employ my ActiveRecord associations in my models, created a few seeds for my database, and checked that everything was working correctly in the console.

Now for the Frontend

I stubbed out a simple HTML page to contain my event information, and set about constructing my first fetch request: populating the DOM with the existing events, and their related users. To save some time later, I also stubbed out a basic CSS stylesheet with some simple background colors for .event, .user, and a few other key items to confirm they were all being appended correctly.

What I WISH I had done at this stage was set up my javascript frontend to take the JSON from the backend and translate it into an instance of a JS object. This created an extra day of refactoring and drying my functions to take JS objects instead of the JSON itself. The code is cleaned up considerably, and I was able to extract a few simple functions into class and instance methods, but starting with an object in the first place would have saved a lot of tedious refactoring.

For the functionality of my application, I needed a simple form for users to submit their name and email. I was able to capture this information using a formData object (appending the event_id to make sure the correct event is updated), and send it to the backend to find_or_create a user. I didn’t want multiple copies of the same user, in the future it may be important to collect all of the Events a User is attending, or create a login scheme for each user that would be based on their unique email. For now, a simple way to take a single spot on the list would do.

I decided to include in the returned JSON the Event to which the User was added. This made it very easy to locate the event by id with a selector, and then find the list of users within. My third fetch request is set up on the delete button for each user. In an active version of this app, this button would only be visible to a logged in Admin that owns that event, and the fetch request to the backend would also check that this event belonged to the Admin before updating the database. For now, with the constraints of a single page to work with, simply being able to access every feature without a login procedure is much simpler.

Instead of deleting the user in the backend, the fetch request updates the user’s event list, and returns the user and event again. On the frontend, we find the user’s div within the event, and remove the user.

Visual Enhancements for Users

To add some visual representations of the event’s capacity, I wanted to use the addition and deletion fetch requests. Since the entire Event object was being returned, I could use the capacity parameter and the collection of users to calculate both remaining seats and the percent of capacity that remained. When the fetch response is returned to the frontend, I added functions to locate and update a capacity counter and a status bar showing the percent of the seats that were taken.

Lastly I turned to the stylesheet to take the application from clown-town to something more acceptable visually. I experimented with transparency and a few color schemes, as well as a few fixed backgrounds that the event div’s could “float” over. I landed on using a stock photo of an open envelope, creating two separate images representing an overlay and a background, and positioned them on the screen so the events “float” out of the open envelope at the bottom of the screen.

Applications for the Application

Collecting user information for organizing an event can take on many particular challenges. I would add features based on the needs of the application user. An admin login process would likely be necessary for most uses, allowing one person to securely update and access the information of users. Being able to send a unique url to a specific event would be helpful for users who want to use the app to plan a party or non-business event. Certain users would need an additional “attending” or “not attending” form field, and the option to submit a message to be sent to the admin of the event could also be useful.

I believe this application could be built out to serve a variety of needs:

  • Wedding Invitations (Extra fields for dining preference)
  • Appointment signups (Music lessons, COVID Testing, office hours)
  • Performance Reservation (Any event with limited available seating)
  • Grassroots Organizing

In any case, the ability to host this application on a website that could create secure instances of an event with a unique URL could be endlessly useful and simple for users to access and submit.

Thanks for reading!

One Step at a Time

Read more about the article One Step at a Time
Photo by Pixabay on Pexels.com

I began my exploration of computer programming with the Flatiron School in July. Learning to code continues to be both exciting and daunting for me. It is empowering to be able to write a line of code that effects a change, or accurately create connections where there were none before. At the same time, the scale of what I have not yet learned looms out of the darkness. Terminology, unknown languages and frameworks, techniques and conventions.

“Aller Anfang ist Schwer.”

German Proverb

This was one of the first phrases we learned German 101 in college. It very simply means All beginnings are hard. Of all the German aphorisms, this one has stuck with me the longest. The bluntness and brevity of the phrase perfectly exemplify a very German attitude to personal growth. There is nothing mysterious about the difficulties of starting from the beginning.

It is hard.

The Flatiron curriculum has taught me to focus on breaking concepts and processes down to their simplest parts. Instead of focusing on the magnitude of a task, I have learned to determine the steps needed to succeed, and start with a laser focus on step one. When those tests pass, move on to the next problem. As each test returns green, soon the lab is complete. I find my flow while coding when I am not distracted by the scale of the entire project.

I urge anyone who is feeling overwhelmed, whether by a coding problem or a roadblock in life, to take on the programmer’s approach. When you view the task or problem as a whole, it is incredibly difficult to appreciate the first step. Especially when starting a new skill, the scope of what you don’t yet know can be an immense pressure.

Commit yourself to reducing any process to its smallest components, organize your strategy, and start with step one. Celebrate each small victory, and keep your focus on what comes next. If your progress is slow or frustrating, keep in mind that all beginnings are hard.

Building an Audition List Database

Read more about the article Building an Audition List Database
Photo by Ylanite Koppens on Pexels.com

It can be daunting to decide what to build for my Flatiron School portfolio projects. It would be simple enough to gin up any number of meaningless associations for a database structure, but what I love about the power of the frameworks we have been learning is the promise of real-world practicality. The excitement of working through a project is the real working thing that you end up with. Coming up with a practical and useful concept can be the hardest step.

As I thought through the many web resources musicians use frequently, it struck me that there isn’t a commonly used collection of current and past audition lists. If you aren’t a musician, here’s the full run down.

The Audition Process

Whenever a position at an orchestra is open, that orchestra will announce the vacancy and post an audition date and excerpt list. These excerpts are selected specifically for each instrument and position, and are small snippets of music taken from the entire orchestral repertoire. The excerpt list can be anywhere from 5 to 50 individual pieces, sometimes with multiple excerpts from each piece.

Musicians who apply for these positions practice these small snippets for weeks and months to prepare for the audition. When the day comes to finally show off that hard work, a committee of current orchestra members will select a handful out of that extensive list, and the applicants will each take their turn (behind a screen to avoid undue bias). After each round, the committee votes on who will advance. Most national auditions have three separate rounds for an audition, sometimes culminating with a “trial week”, where the applicant plays with the orchestra during a full service week.

All of this to say the audition list and the excerpts on that list play a big role in any musician’s preparation for an audition. More than that, having a sense for what excerpts are called most frequently can guide the arduous process of preparing a particularly expansive list.

So… How do you keep track?

The current scope of resources online includes a variety of audition posting websites, such as Musical Chairs. These sites provide listings posted by orchestras big and small, in addition to non-performing job postings. However, there is no data about the excerpt lists on these sites, and a musician does not have access to any previous auditions for comparison.

Solution: the community needs an audition archive!

So Many Models!

To thoroughly provide search-ability for this project, I needed an extensive web of models.

  • User
  • Piece
    • Composer
  • Excerpt
  • List
  • Position
    • Instrument
  • Orchestra

The Anchor of the model structure would be the almighty List. Lists would be associated with a position and an orchestra, and through that position lists could be searched for by “instrument”. Lists would connect to the “music library” by having many excerpts, which belong to a piece.

The longer I spent on the model structure, the more I realized I would want each of these attributes to be its own model to facilitate simple searches, and also allow for methods to find the most frequently called excerpts for an instrument, or most common piece asked for by an orchestra. I also wanted to give users the ability to create their own collection of lists for easy comparison.

Thank Goodness for Rails Generators

With such a spiderweb of models, the rails model generator saved me countless hours when stubbing out the database. Still, implementing and checking these connections was time consuming. Once I finished setting up the associations, I set up all my CRUD (CReate, Update, Delete/Destroy) methods for each model.

This left the flow of the site in quite a mess, so I headed back to the drawing board to define the flow I wanted for my users. I ended up simplifying the flow to focus on rendering entries by instrument, and focusing the user on their own lists. The rest of the routes dropped into a route called “Library”, to allow curious users to explore and admin to edit.

The most satisfying part of the project was creating a custom stylesheet for my website. Simple but specific google searches brought me to some powerful css options, allowing me to create a clean and organized view that I hoped would represent this complex information without being too hard on the eyes.

Simple shadow boxes and a two column design made navigating my new database simple and intuitive.

I found myself at the limits of my programming ability when attempting to implement some kind of dynamic collection_select fields for excerpts. As the database grows, selecting the piece for a new excerpt from a drop-down menu would be very tedious, and I wanted to give the user a composer selection that would then dynamically populate the piece options. After some detailed searching, I found that the way to accomplish this was through Ajax and javascript, and none of the resources online provided a time-efficient implementation for someone who had no experience with this feature.

My goal for this project extends out beyond the Flatiron Portfolio. This app has serious utility for auditioning orchestral musicians, and my goal is to continue to streamline the project and someday make it live through this website. Musicians, stay tuned! For now, it’s on to the next module.

Putting it “Out There”

Read more about the article Putting it “Out There”
Photo by Negative Space on Pexels.com

A topic of fixation in our hyper-connected world is web presence. The metric of success personally or for business can be measured in likes, views, retweets, and trending. As a self-identifying introvert, I find myself caught between sharing my passions and interests and protecting myself from the inevitable exhaustion of feeling constantly scrutinized.

As a musician, I find it challenging to “submit” a project to this public space. For one, the standard of perfection that is expected for recorded music makes it challenging to achieve an acceptable product. But on a deeper level, I have come to discover that I never feel ready to post for another reason: I never feel like my product is ever “finished”.

The pursuit of maintaining and refining the fundamentals of brass production is a bottomless pit. A feeling of incompletion nags every long tone session. New tendencies need identification and diagnosis, while old habits look for opportunities to sneak in through the back door. Why post anything at all if you can do better? It isn’t just the imperfections in a particular performance, but the dormant potential that one more week of grinding might cure that persistent hangup.

During quarantine, when the world of live performance came to a dead stop, I found myself searching for a sense of purpose, or at the very least a project to occupy my mind. I have never thrived when left to practice in a vacuum, and without the regular projects and programs to prepare it didn’t take long to feel burnt out and exhausted. I felt driven to build and create, and endlessly chasing the sound and facility I wanted in my horn playing was not satisfying when my only outlet was my practice room.

Enter: The Flatiron School

My search for a project to divert my energy led me to the Flatiron School. They offered a zero to hero online course promising proficiency as a front and back end web developer. I jumped straight into their free “Bootcamp Prep” course for software engineering, and was soon addicted to the satisfying green text of a passing test. After some study and practice, I could make things happen with a simple line of code, and a solution to any problem could be found in the lesson material or in easily searchable online guides. I applied for the self-paced program and dove head first.

Maybe not surprisingly, my resistance in sharing my musical projects was quickly manifest in my programming. The scale of what I knew how to do as a baby programmer was constantly dwarfed by the remaining curriculum, immense online community, and sheer number of languages I wouldn’t be learning through the program. I avoided the school’s Slack channel and study groups, unwilling to interact and discuss topics I had not yet mastered. I found myself in the same bottomless pit, frozen by a feeling of inadequacy.

Fortunately, the Flatiron School’s holistic program includes one-on-one sessions with educational and career coaches. A recent session with my coach (Thank you Valerie!) gave me some important perspective on my goals in the Flatiron School, which I hope to also apply to my work as a musician and web designer.

No project exists in a vacuum

Very few roles in life are truly isolated. We all depend on others to thrive. Cultivating a supportive community can provide numerous benefits, including support when the going gets tough. Both music and programming are necessarily collaborative pursuits, and that skill of collaboration needs just as much practice as the fundamentals of either.

The goal is growth, not perfection

Pursuing perfection has been a fickle and frustrating objective in music, and this reluctance to share my efforts until I am completely satisfied has been a significant mental block that has translated to many other parts of my life. With that in mind, I am enthusiastic about using this platform to share my progress as much as my products.

I hope this overly long post will serve as an inspiration to others who feel the same way. Be empowered to share what you do and love.

Symphony Service Tracker

Read more about the article Symphony Service Tracker
Photo by Pixabay on Pexels.com

A Sinatra App

During the pandemic, a serious problem for the Houston Symphony as musicians return to the stage is the balance of work. Each program that the symphony does will have a certain number of rehearsals and concerts associated with it. These are called “services”, and it is the simplest way to track how much work a musician has done over the season.

Managing each section involves attempting to keep these service counts similar. Making sure one musician does not play every program while others barely get any stage time is crucial to keeping morale high and avoiding injuries from overuse.

With that in mind, I set out to create a simple database and system of views to allow an average user to navigate each section and compare service counts.

Each musician belong to a section, allowing sections to have many musicians. Each musician will have many programs that they have performed, and each program will in turn have many musicians. This required a join table, which was simple enough to set up.

I created views to allow a user to see all sections, all musicians, and all programs. These will likely be the most used pages of the app for an average viewer who wants a general sense of the breakdown of services across any section, or to just see general info about each program. To allow for quick navigation, I assigned the name of each section and musician to its id page to allow a user to get more detailed information.

Since we don’t necessarily trust our musician colleagues with the responsibility of adding to, editing, or deleting from our database, we hid these features from a user who is logged in with a user_id. I decided to create a separate class of login for an admin, and reveal the new, edit, and delete buttons only when an admin_id was detected in the session hash. The admin signup process occurs when someone enters an admin key into the appropriate field in the signup form, and their account exists as the separate class Admin.

The security of this process is certainly not very robust, and I would never trust the sensitive data of payment info or personal info with this level of key check. However, for a simple logistical app for orchestra management to use, I think this would be apropriate. When setting up the app for an institution, a unique key could be defined within the Admin class to give more protection to the admin capabilities.

I enjoyed doing some rudimentary css selecting and modification to get the index views of sections, musicians, and programs to be a little easier to parse visually. I could definitely see this kind of program being an asset to a personnel manager who wants a simple interface for visualizing how each section is balanced as far as service count.

If I were to polish this project up for some kind of release, I would do some more beautification to the styles to make the information even more digestable and adaptive to mobile use, and add the ability to record a subset of the services of each program for a musician. Not every musician plays every service of a program, and being able to define the services played for each program would give an even more accurate sense of that musicians involvement week-to-week.

Symphony Website Scraper

Read more about the article Symphony Website Scraper
Photo by Negative Space on Pexels.com

A CLI Data Gem Project

I decided to focus my CLI Data Gem project on the Houston Symphony website.

Most orchestra websites are clunky at best, and impossible to navigate quickly at worst. I decided to create a program that would scrape data from upcoming concerts from the website and provide a neat and tidy presentation of the important details through the command line. Additionally, I wanted to allow the user of my application to search for their favorite composer, and return a list of concert programs that they could easily select and view more details about.

I first created my basic gem structure using the bundle method. This was a great learning experience to be tasked with the basic file structure that was always a given through the Learn coursework so far. My first attempt at the folder structure used the gem name “houston-symphony-concert-cli”, but this created a rather confusing nest of folders. I ended up circling back to this setup method after writing a few classes, and arrived at a more concise folder structure. Towards the end of my project, I moved all the class files into a module, which allowed me to clean up the folder structure further, but required rather messy method calling in the CLI class. If I had to start again from scratch, or had another week to continue refactoring, I would choose a much tidier module name.

I started my work on the scraper method. After poking around https://houstonsymphony.org/tickets/concerts/ with the inspect tool and trying some different selectors, I found that I could scrape both the concert dates and a descriptive title for the concert. I also worked out a way to store a “program url” that a second scraper method could use to collect a detailed description and a list of pieces and their composers.

Then I formed classes to describe and keep track of each Concert, Composer, and Piece. Similar to the music library lab, each Composer had many Pieces. In addition, each Concert had many Composers and Pieces. My first few versions of the code to transfer from the scraper to each class centered around the scraper creating a hash with the necessary data. As I refactored, this process obviously slowed down the scraping process, so I added create_or_find_by_name methods to the Composer and Piece classes. Plugging the scraped information directly into a call for a new instance of each class halved the “load” time of the concert database.

After getting the classes to play well together, it was on to the CLI. I had been using the bin/console command to run tests on the classes as I wrote, so this file was quite messy. I ended up creating a new class for the CLI to hold the many methods I had in mind, leaving the console folder nice and clean.

I knew I wanted the user to be able to search through all Concerts for a specific Composer, but my first task was to simply create a display function to puts out the details of any Concert. Simply iterating through each Concert’s Composers allowed me to output an array of Concert dates that included the desired Composer. Selecting a concert for further details simply calls the display method on that concert.

The other two main options on the menu were more straightforward: displaying the next upcoming concert was simply displaying the first Concert from the list of all Concerts, and a straightforward list of all scraped concerts which could be selected for more details.

During this process I continued going back to the video resources in the assignments page for reference. I think I would have ended up spending much less time refactoring overcomplicated code if I had started from the CLI side and then working backwards, rather than starting at the scraping methods. Writing first the simple code that I “wish I had” would have saved me the trouble of over-engineering solutions to a few problems, and cut right to the utility that the many interconnected classes provide.

I also had some struggles using the sandbox IDE to code my program. This browser based system is very useful when loading up a small lab with everything ready to code, but it proved clunky when trying to load my code back up after a break, or if the wifi connection was lost. I spent many afternoons re-coding certain chunks because they had been lost to a wifi disconnect!

My other frustration with this project was based on the inconsistent formatting of the Houston Symphony website. After finding a very tidy chain of css selectors to differentiate the Composer and Piece names on certain pages, I discovered that some of the concert detail pages did not use the same parent-child relationship between these items. I had to create a second high level if/else flow to avoid scraping musician names or piece names as Composers, and these pages did not allow me to include the piece data for those concerts. Even without this, the functionality of the composer search still worked, but the scraping method became very clunky with so many if/else flow controls.

I would love some feedback on my scraper methods. This is where I thought the program could still be streamlined, as scraping the entire season (40ish concerts) takes nearly a minute. Perhaps a more conscice css selector would also be able to differentiate between the Composers and Pieces on those pages where the piece names are not contained in a separate ‘b’ container.

Overall it was a very satisfying process to go from a blank slate to a program that pulls interesting data from a website I use very frequently, and populate a series of classes allowing for easy searchability. I can’t wait to see what is next and start working in a localized environment!

End of content

No more pages to load