Healthy Habits

VIEW ON GITHUB

General Summary

Healthy Habits is a Spring application that allows users to go to a website, make an account, enter habits and behaviors they would like to track, and view those on a graph so they can see their progress over time.

My main goal with this project is to display my competence as a developer and I was looking for a realistic project I could build by myself that would combine all of what I'd been learning about programming and software. The use-case goal of this application is to help users track their behaviors in a way that is insightful and helpful for their progress as individuals. I personally use it and have found that it helps me achieve my goals faster, and motivates me in the process.

What was it built with?

Healthy Habits was built with Java, Spring, Hibernate, MySQL, CSS and JavaScript.

How does it work?

The application follows a MVC design pattern using Spring to achieve the end-goal for the application. Along with Spring, Hibernate is used to store persistent data in a MySQL database. Thymeleaf works in conjuction with the view layer to display models that are sent and received between the user's view and the server. This allows the user to generate content and visualize that content in a meaningful way.

Spring Security is used to authenticate users and BCrypt is used to encrypt passwords that are sent by the client. Spring Security allows the program to require authorization for users to access certain pages. In order to be authorized, the user must have an account and be logged in to it. If the client accessing an authorized-only web-page is not logged in to a valid account, they cannot access it.

How is the code structured?

The application is separated into 5 different sub-packages. This makes it easy to navigate and understand for both myself and any other developers who would potentially be working with it.

Controllers

There are several controllers used in this program. If you want to see them all, feel free to check them out on GitHub.

HabitsController

The HabitsController class enables registered, logged-in users go to the "/habits" url in order to add or remove habits to their account. The class has an autowired field for CustomUserDetailsService that allows it to fetch the authorized user browsing the page, and also one for UserService so users can be saved/updated. There is a get method and a post method for this controller. The get method returns the "habitspage.html" file with two models, habit and user. The post method places the habit model returned in a try block and uses an if-statement to determine if the habit is being added or removed, then triggers the logic necessary to carry out the user's desire. After that, the user is saved and the habitspage is returned if no errors are caught.

VisualizerController

The VisualizerController class simply obtains the information needed for two models. The user object contains all of the information needed for the visualizer view. Two models are created instead of one to separate the user and the user's habits for simplicity in the HTML file.

RegisterController

The RegisterController listens on the "/register" url and has a get method and a post method. The get method returns a new User model with the name "user" for Thymeleaf and the register.html page view. The post method obtains the user model that is sent when th e page is submitted. It then uses the DataValidation class to check if the username and password are valid. If they are, then the user's password is set to an encoded password by the PasswordEncoder. Then, the UserService calls the addUser method and passes the user model object. This method's code is in a try-catch block, so if it encounters an error it returns the the error page.

Repositories

The UserRepository is an interface that extends Spring's CrudRepository of the object type User and id of Integer. The UserService class uses this repository to manipulate the data being stored by the application.

Security

The ApplicationSecurityConfig class is used to configure Spring to behave how it needs to behave to meet our requirements. It customizes two aspects of Spring, the AuthenticationManagerBuilder and HttpSecurity. The AuthManagerBuilder needs to know the right UserDetailsService in order to function properly. Since a custom one is used in this application, the CustomUserDetailsService is autowired and set as the UserDetailsService.

The HttpSecurity configuration method is set to authorize requests on a certain set of pages where it's needed, like login, visualizer, habits and logout. This secures those pages so they cannot be accessed by someone who should not be able to see them.

Services

CustomUserDetailsService

The CustomUserDetailsService implements Spring's UserDetailsService and overrides the loadByUsername method. This is needed because the UserRepository interfaces locates users bsaed on their unique integer id, not their username. A lambda expression is used to parse through all users to find the one that matches the username that is authorized to view that page.

UserService

The UserService fetches and stores data using UserRepository interface. The UserService what is used by other classes and methods when they need to get or store user information, rather than dealing with the interface directly.

DataValidation

The DataValidation class is a utility for checking user-generated inputs to ensure that they meet the requirements and are not malicious, such as inputs containing special characters, or extremely long inputs that would impact server performance and user experience if it happened at a large scale.

User

There are two classes in the User package: Habit and User. These classes represent the data that is persisted in the application.

The User class is annotated as an Entity so that JPA knows it should be persistent. The class has 5 fields as follows: userId (int), username (String), password (String), roles (String) and habits (List). The userId is the unique identifier and it is generated sequentially. The habits field is a list with a OneToMany relationship with the Habit class. One user can have many habits.

The Habit class is annotated as an Entity as well. The class has 5 fields that define the different states the habit object can have. The 5 fields are name (String), ratings (ArrayList), dates (ArrayList), habitType (String) and user (User). The habitType variable is annotated as Transient so that it is not persisted, since it is only used for determining if a habit is being added or removed. The user variable has a ManyToOne annotation that indicates there can be many habits to one user. The ratings field is an array list of 0/10 ratings that display in the order that they were added by the user. The dates array list is a collection of timestamps in millisecond format. Every time a habit is created, the current date in ms is added to this list. Deciding to go with dates in ms was done because that was the easiest way to feed that information to Highcharts without having to manipulate it too much, and getting the current time in milliseconds is as easy as calling System.currentTimeMillis().

Resources

CSS

The styling includes a couple images to fit the theme of the application and makes the website easy to navigate and use.

HTML Templates

Most of the HTML templates are not noteworthy other than the fact that they include Thymeleaf references which allow the view-layer to obtain and display models based on Java classes. The visualizer.html page contains a graph that models user data however, which I will explain below.

The habit visualizer page uses JavaScript, Thymeleaf and Highcharts to display the data relevant to the user. In order to display the habits a user has in a way that the chart understands, I created two JavaScript variables. One of them is a blank array list of data, and the other is an array list of the user's habits. I used a for loop that iterates over the user's habits and creates a habit variable with two fields: name and an array list of data. It then iterates over a specific habit's ratings to then push that data to the array list in the habit object. After all of the user's habits have been iterated over and added to the data array list, the Highcharts function is called, and the data variable is given to the series parameter so that the graph has the data needed, in the proper format.

Admittedly, figuring out how to display this data in a way that Highcharts understood took a while for me since this was the first project that I dealt with displaying Java-originated objects on a website. I figured it out in the end though and achieved the desired end-result, so that's what matters!

What I Learned

While creating this project, I was pushing my limits as a developer. I got stumped on parts of the application several times, but I didn't give up. The purpose of this project was to strain my abilities to the point where I had to grow my skillset in order to solve the problems at hand. It helped me develop a much higher level of competence and also confidence in my own abilities. I began the project only knowing Java and SQL, but through creating the finished product I learned how to build the front and backend of a web application as well, with HTML, CSS, JavaScript, Thymeleaf and Spring.

The most important thing I learned while developing this project was that foundational understanding of what you are working with is extremely important in order to solve problems. Any time I was stumped on a problem, it was with a technology or method that I was not knowledgeable enough in. For example, how do I take a user's habits and display them on a graph? In my googling process, I learned that Highcharts was a useful and easy way to display data on a graph, so I started implementing that in the application's HTML. After I got the graph to appear, I realized that I did not know how to link the data my application was generating to the graph. I could hard-code data on it easily, but using the data generated from the backend and then translating that to JavaScript in a way that Highcharts would understand proved to be difficult for me.

I realized that the main issue was a communication issue between technologies. I understood how to convey an object in Java, but not JavaScript. This lead me to take a break from development to go learn what I needed to know about JavaScript to manipulate data it received from Spring's models and create new data structures. Java objects are easily converted to JavaScript objects, but the problem was that Highcharts needed data stored with certain key-value pairs for it to understand what it needed to display. This was done by creating new JavaScript variables based on the Spring models it received. I iterated over the objects it received and put them in a new array list that contained keys and values that matched the Highchart variables needed to create a series of line graphs.

If I were to create the same application again from scratch, it would only take a few hours. A lot of the time spent on this project was trial-and-error and the learning process, which cannot be skipped on the road to mastery. I'm grateful for the growth and understanding that occurred during the development process.

VIEW ON GITHUB