Latest Entries »

Today I began my usual planning and preliminary work on the next section I’m working on, the personal info view. This view will eventually allow the user to change their username, e-mail, and password. Of course, that will need the login screen to actually be reactivated, so that there is something to change!

As part of the planning, I basically just looked at the existing classes to try to see how the user information itself is going to work. I know that at some point we will be reactivating the login screen. As such, I tried to look to see if there were any existing objects containing the user information. Although there appears to be such a user class, as well as a login class, I did not see any actual instances, which will likely be created for the application when we reactivate the login screen. This means I will likely create this personal info page with a dummy user at first, until I can integrate it later with the login screen.

Knowing that, I started to think about how the layout was going to look like. The screenshot shows an initial creation of the view, though I am debating about whether to do it in 3 sections or just the one. If I keep the current layout, the current data will be displayed on the right side as the second information block on the button, and clicking the button will bring up a screen where the information can be edited via keyboard.

The next part is to create the dummy object and figure out how the edit screens will work.

Hm, forgot to post this yesterday. The last two days were spent fixing up the last of the previous changes and finalizing that they work, the preferences section. One thing that had not been done in the last iteration on them was making each area editable. This was fairly straightforward, where I just had to set up the initialization to take the existing data and start up that way. It got a little bit more complicated when returning, where it would add it, but would also need to remove the old entry.

My initial attempt to do this seemed to work, but caused several odd issues with areas seeming to disappear because of when I was removing cells and reloading the table. Fixing this did not remove all of the bugs, and also showed me another bug where it would remove the cell even if a new one was not added, such as if I simply cancelled the edit window. To fix both issues, I reworked when I was removing it to only happen on return, with conditionals for both cancelled and completed generators.

After getting this to work for the simpler food and wine preferences, it was pretty easy to extend my changes to the recommendation preferences as well. After fixing that, making sure it works on both devices, and fiddling with the timing to make the transition smoother, the preferences looks good now.

Another issue that popped up was from Evan’s testing, where he is currently working on a new split view of the menus, which has been floating around for a while but is finally being implemented (a week before it needs to be shown, no less). Because of the incredibly annoying way the multi-cell view does not receive the autoadjust, it had to be tweaked carefully for each orientation on each device, using a series of conditionals. All of this is further complicated by the new layout, which introduces yet another view on top of it as well as a different view size to boot. Basically, it all broke again. Evan said he would take a look at it, because he wanted me to focus on the next area, which is to create a Personal Info view, where the user should be able to view and modify their username, e-mail, and password.

Trying to quickly finish the existing issues with the multicell displays and preferences stuff so I can get started on the new tasks. The multi-cell work went fine, which is probably because I’ve gone to war against it so many times that I’ve pretty much memorized which issue, section of code, and fix is needed for each error. Just needed to resize it on iPad, because it worked on iPhone, which is probably what made it slip past me the first time. The menu view had some additional rotation issues where there were really weird resizing issues when coming back from the Twitter window, but I had already encountered the same type of problem before and quickly fixed that up.

Hoping to look at Preferences tonight/tomorrow, where I need to make the various options editable and also do a final check of everything to make sure no lingering bugs are around. Wednesdays are always unpredictable because I have unpredictably long meetings for my SD&D project and I still need to finish up some other assignments for Friday, so we’ll see how it works out.

Today’s All-Hands meeting was interesting, although most of it didn’t really apply to me such as budgeting and travel changes. They are moving them to a monthly meeting because they felt that the time wasn’t really being used well, what with people reading e-mail and such. I admit that I was actually working on Wine Agent during the meeting, but I was paying attention! They’re going to try to have status updates from everyone at that time as well. My only thought about the idea was that it seems like the meeting is going to overload now. It will take time for everyone to give their update, and then I assume that presentations will all be compacted as well, but I guess we can’t know unless we try it out.

So this week I’m switching back from the reasoner part of the project back to the iOS UI and functionality, to try to get it all looking nice and working right in time for the hoped for demo. I talked with Evan today about some of the features we should have done for the demo, and looking at what has been done so far. As the first step, of course, I need to finish cleaning up some of the remaining issues left over from my work over the Winter.

This first part includes making sure everything rotates, fixing the multi-cell table view, and cleaning up the preferences UI, before I move onto adding more functionality relating to the Facebook, Twitter, Personal Info, and Preferences areas. The rotation needed more work because I somehow missed the fact that some of the restaurants actually do have menus listed, which were not rotating. The eternally breaking multi-cell table view is breaking yet again because I finally stumbled across the case that causes the 2 case to occur. I had known about the fact that it supposedly had this case, but I had never run across it during my testing..until today when showing it to Evan, of course. That’s how Murphy’s Law works, after all. The Preferences UI still has some remaining issues, such as needing to make each entry editable.

The rotation was pretty easy, and I fixed that in a few minutes after our meeting. It was a simple matter of editing both related views to rotate the same way as the others, with a bit more work on the dish description view because of the multi-cell view, but I have wrestled with that before, making it a simple fix as well.

The multi-cell fix is next. As always, I expect to face a few hours of mind-numbing bashing of the code as everything about it breaks until I find the right combination of tweaks to make it work in all orientations on both devices. Fun. That thing is seriously Murphy’s Law in the form of code…it breaks something, somehow, anywhere it appears.

The preferences UI change to make it editable should be easy, barring any catastrophic failures. Just need to get it to initialize the screen correctly so that it will have the existing info and then work normally from there. We will see.

The issue is really just finding the time to work on everything. For anyone considering taking a full courseload, getting a part-time job, and doing undergraduate research work at the same time, I recommend you get a time machine and get used to the fact that you’re going to be in a constant state of busy and panic.

So today had quite a lot of stuff TWC-wise; the weekly all-hands meeting, the Wine Agent team meeting, and a bunch of work with Momo on the iOS version, getting that ridiculously annoying (and slightly embarassing) bug with the Bluetooth. Plus some thoughts on Watson. Thus, a totally disorganized post appears!

I had noted on an earlier post that I had stopped posting summaries about the all-hands meetings, partly because there’s much more detailed summaries on the meeting pages themselves on the TWC wiki, partly because I’m not actually sure if posting about our internal meetings is good, and partly because I usually don’t understand half of the terminology still. I’ll say a bit about today’s though. Today’s talk was a practice for the CogSci seminar, which is where I first heard about the TWC and its applications, and was about semantic applications in the domain of financial services and the business world. A pretty key distinction, I think, was that it was not trying to open data, due to the inherent…I wouldn’t say need, but certainly benefit, in keeping data to yourself as a financial business. Instead, the talk was about leveraging currently open data from public domains or information they themselves make public for various reasons, and integrating semantic applications with that.

Slightly off-topic, I wish I had been at the TWC talk with the IBM researchers working on Watson, because I’m sure there was a lot of neat talk about semantic applications as applied to a super-reasoner like Watson. Seeing Watson make Ken Jennings and Brad Rutter look slow was pretty incredible, and probably makes natural language researchers everywhere weep with joy. The way Watson was designed, it combines a whole slew of different algorithms and techniques in parallel to enable a very balanced reasoning system using NLP, but I’ve never really heard about NLP mixed with semantic applications (at the meetings anyhow), so it’d be interesting to hear more on that. Thinking about it, I wonder if it’s because semantics are intrinsic to natural language, so it is already integrated with it?

At the Wine Agent meeting, we talked more about where the project is going and what we need to get done. Evan mentioned that someone needs to make a Java parser, and although I think it’s something I’d like to do, I know that I won’t have time to work on that as well as the iOS work and balance my classes to top it off. At the moment, the iOS implementation of the reasoner is higher priority than the parser, I think, but I’ll probably keep it in mind (unless someone else does it, I suppose).

With the cursed implementation of Bluetooth functionality for the iOS Wine Agent, it turns out to have been a totally ridiculous reason…our debugging was breaking the code. It turns out that we actually HAD fixed the code, somewhere along the line, but in doing so, the various alerts we placed around the code got disorganized, to the point where a declaration moved somewhere it shouldn’t…and the debugger didn’t catch it. We had to do a backtrace to figure out that it was actually stemming from the alert. On the bright side, I figured out how to see the backtrace…my method of debugging has always been reliant on light breakpoint use and lots of printing/alerting, not GDB, especially since I never really had time to explore the details of Xcode or Objective-C other than what I ran across in my work. The biggest downside of working on existing code is always that you get a kind of lopsided and spotty knowledge of whatever the language and tools needed are.

For this week, Momo is going to focus on putting the rest of the Bluetooth sending/receiving into Wine Agent, and I’ll be working on some of the reasoner implementations. This is reversed from last week, and we decided that this seems like a pretty good idea, to rotate what we’re working on. It keeps us from collisions in our work, and also makes sure we both know what’s going on with both parts of the iOS work.

The last week has been spent trying to get the two devices to finish setting up a Bluetooth connection. This should have been pretty easy, as there is what looks like an example in the documentation. It was going very smooth, and both devices can find each other and initiate the connection dialog. However, I ran into tons of issues with one of the devices freezing and the other crashing when the Accept button is pressed to finalize the connection. It turns out that Momo, who made a standalone test app for it instead of integrating it with Wine Agent, got that to work somehow. After today’s meeting I looked over my version on Wine Agent to try to figure out what is going on with it. Unfortunately, we had no luck, though we managed to reverse the order of the crash/freeze and even managed to get both to crash and both to freeze. Every combination but what we wanted. Our code looks to be almost identical, and at one point in the testing appeared absolutely identical. So it is still a mystery.

Once we can get that working, we will probably stop working on the Bluetooth side of the distributed reasoner for now, focusing instead on the actual reasoning itself. Evan noted that even if we can’t get the distributed part to work and if the Bluetooth functionality isn’t complete, it would still be great to have a full reasoner working (the current reasoner built into the app is more of an ad hoc, less flexible reasoner).

Today I worked on setting up the actual classes and calls to get the Bluetooth functionality working. Due to the storm I haven’t gotten down to the lab yet to pickup the second device to actually test if the peer picker works or to do any data transfer tests. This means that today was simply me trying to get everything set up for that time.

I started out by just figuring out what I needed to add the peer picker and get the searching dialog to appear, although it could not progress further without an actual peer. This led to the above result, which went considerably smoother than I expected, and helped me think about which functions I would need to flesh out later in order to add reasoning and processing of the incoming data. Now that I knew how I needed to set this up, I needed to move this into its own class/view controller so that it could be added in other places and also so it would not clutter up the Recommendation engine view controller that I was currently testing it in.

This took a while, as I needed to make a clean class for the group recommendation view controller and add code so to get the dummy table working, but it went smoothly. It is currently just set up for testing purposes, obviously, so it just consists of an empty page with a button that when clicked pops up the peer picker dialog as it searches for nearby peers.

On Friday I will have the second device, and will be able to set it up so that this view will simply start up a search as soon as it loads (possibly, depends on how the picker looks/works), and then I will be able to explore the actual peer picker UI and see if I need to change anything with that. Once I finish getting it to successfully connect to a peer, I will be able to proceed to some simple tests, where I’ll get a button to just send a message to the peer, which should receive the message and pop it up on the screen. This testing will scale up slightly to send custom objects, since we will likely either send the data for the reasoner in some form of RDF or as one of the bundled arrays that the application already uses to pass data between views.

As part of the discussions/e-mails with Evan, he noted that he expected that there would be some ‘Reasoner’ class, with a ‘RemoteReasoner’ derived from it that would have some sort of ‘CommunicationChannel’ object to abstract away the actual communication protocols. It seems to me that this is partially done. The newly made view controller is pushed on top of the view, and could be the Reasoner class if I implement all of the reasoning calls in it, regardless of how the app is connecting to the group. Alternatively, if an extra layer of classes was added under, this class would call the peer picker and upon connection delegate the reasoning to either the class handling Bluetooth or some other protocol, the RemoteReasoner? The CommunicationChannel object is pretty clearly going to be the session object that I have to use with the peer picker.

One thing that I know will have a large impact on how the rest proceeds and how the peer picker is called will be how multiple peers are connected. Does the peer picker connect to multiple peers at once, or do you have to reopen the searcher each time? I’ll have a better idea once I can actually test with the picker.

We had the first meeting of the semester for everyone working on the Mobile Wine Agent, which is Evan, Dan, Momo, and myself. We basically just spent the time talking about another important feature we need, which is the ability for group collaboration via Bluetooth. Although the recommendations and preferences themselves are still under construction, once they are complete it will be essential that group features be available.

The plan is basically to set it up so that any users of the application will be able to start a group recommendation, connect with their companions, and share their preferences/recommendations in order to (hopefully) get a result back that everyone will agree on, based on them. This will be done via Bluetooth so that this connection can be done locally (and the reasoning) so that the feature will not be limited by WiFi access or a connection to the server (though the iPhone/iPad are both still limited to server reasoning currently…which I still can’t connect to for my own testing, ironically enough).

The general outline of how this will work was drawn out. There needs to be functions/calls between devices corresponding to adding/removing/querying data and for the final result from the device’s reasoner. We hope the devices will be able to update each other on the user’s data, querying them to check things, and get a solution.

Of course, the details are still largely fuzzy, as this was just a general planning meeting, before any of us even looked at the feasibility. We tried to come up with issues we may have missed, such as a few involving multiple devices and their connectivity (or lack thereof) as related to reasoning and the major issues we will likely run into with the Bluetooth API/protocol itself, such as the data formats we can send, and whether we will be able to have interdevice functionality between the Android/iPhone/iPad versions.

As for feasibility, I spent a while looking this up afterwards, trying to find more information on the iOS Bluetooth API, what it would allow to be transmitted, a bit on how that works so I could think about implementation, and whether iOS to Android connections would even be possible.

The results upon searching for an iOS Bluetooth API seemed unfortunate at first, with almost every result being someone asking whether it even existed. More outdated results either said no or mentioned that it only applied to audio, not data, neither of which was a good sign, but I eventually found that it does exist.

Among the components of the Gamekit package, designed to help developers making networked games, is Peer-to-peer technology, which allows for communication of data over Bluetooth or WiFi. Some quick examination of the documentation looks promising; it says that although it was geared towards games, it can be used independently in any kind of application, and that the data being sent is defined by the user, which is very good news.

It appears that the implementation will involve making and displaying a peer picker controller, which in turn will create a session object. This session object will be configured in a bunch of ways (just looked at it all quickly to try to get an idea of how it works), and will be the point at which all data passes through. The data transfers will cause the session to call delegate methods, which I think will be implemented in the peer picker controller. The peer picker controller will also be where the user will be able to choose from other sessions to connect to for the group recommendation.

As far as some of the other considerations we discussed, a few seem pretty covered by the API. The unique peerID is built into the session object, and a display name may be set. I imagine that the implementation will have the Group Recommendation button pop-up some kind of prompt for a display name and then move into the peer picker controller view, or the display name will be set on application start and the button will just move straight into the peer picker and init the session with the display name automatically. Also, there are methods available for sending to all connected sessions as well as to a specific one (according to peerID).

Much less fortunate is the plan to have the iOS versions work with the Android versions. Because the Bluetooth capability works via the Gamekit package, it already seems highly unlikely that its methods to find peers would register the Android version of the app, much less actually connect to the Android version of its ‘session’. Looking it up to see what others have found, it seems to be true, with some having slightly more detailed explanations noting that Gamekit will only work with iOS devices or MFi compatible devices (meaning Made for iPhone/iPad). Which doesn’t include Android, apparently.

To sum up, I think that I can start working on some tests to get Bluetooth capability into the iPhone/iPad applications and eventually to the actual Group Recommendation work, but that cross-device compatibility might be outside of the API. I’m not sure if we could actually finish the Group Recommendation functionality right now, however, since the Make a Recommendation function does not work for me yet, and apparently still uses the server anyhow when working for demos, and because the Preferences is also not complete yet. All three could end up in some sort of pseudo-finished state though. I really wish I knew how to get the search/recommendation functions to work on my end, especially recommendations when I reach the point where I want to send recommendation data across the Bluetooth connection. For that matter, it seems we will only be able to test the Bluetooth capability when the group gets together at the lab, because the Bluetooth capability will apparently not work on the simulator, only a device.

Today I worked entirely on the Recommendation Restrictions part of the Preferences UI. I started out by adding modes to the recommendation view to allow changes of the navbar buttons and title and for any later changes that would require differences between using the class for making a recommendation and when using it for recommendation restrictions (in the Recommend tab and User tab respectively).

When working on the navbar buttons, I realized that in order to stay consistent with the other views in the app I should probably make this a modal view instead of the current pushed view, and add the Cancel/Done buttons to it instead of the current default left navigation button and right Commit button. To do this, I needed to build the modal view in the same way as previous changes by first making the new navigation controller for it (so it has a navbar) with the view as root, and then presenting it modally. I added the buttons to the view as calls to the navigation controller, which calls functions in the view which in turn call the delegate’s processing functions to both dismiss the modal view and get the data to where it needs to go. In this case, that meant making and specifying the delegate for the MakeRecommendationViewController as the UserPreferenceManagerViewController.

Once I got the delegate stuff working correctly I was able to give the Cancel button its functionality. At this time, I also realized that the change to a modal view also broke the buttons that would bring up the description generators for each of the Dish and Wine descriptions. This was due to the way it was calling it up, where it previously presented the modal view on the application’s current tab controller. However, since it was now in a state where the modal view was being shown from a different navigation controller, this call needed to change. Using the modes I made before, I added a new set of rules so that the right calls for the modal presentation and dismissal were used.

After this, I could finally begin working on the Done button’s functionality so that it would return correctly and update the main view. At the time, I just had it so the Done button would dismiss the view but didn’t actually do anything. After considering a few ways to return the data, I tried to go for the simple solution by creating a NSMutableArray and inserting the two NSArrays that hold the NSArrays of Pairs that return from the description generator. I again had the various layers of this object outlined on paper because it was so convoluted and frustrated several attempts to call certain sections of it.

Returning this NSMutableArray to the delegate, which is the UserPreferenceManagerViewController outside of the current controller which is dismissed on return, seemed to make sense. It would be added to the main view’s NSMutableArray of restrictions, making the final chunk of data a NSMutableArray of NSMutableArrays of NSArrays of NSArrays of Pairs. However, when I implemented it this way it failed despite several stabs at different ways to return it, thinking it was my method of return.

After a few failures, I went back and tried simplifying the data being returned to see at which layer the return would start working. As it turns out, I found that the data would write fine to the NSMutableArray once I hit the NSArray layers. Confused, I went to the ever useful documentation to try to figure this out. Basically, the fact that NSMutableArrays used the addObject function to add data was no lie, it literally will only add objects with NSObject as a superclass…which does not include NSMutableArray itself.

I knew, then, that I had to use a different container, and the obvious choice was NSArray. Looking at the NSArray documentation, it was simple enough to change it to save the two NSArrays of NSArrays of Pairs into a NSArray instead of a NSMutableArray, and this NSArray could be stored successfully in the NSMutableArray. Now that’s a lot of arrays.

At this point, the Done button was now successfully writing the data to the array holding the recommendation restrictions, but the display was still showing my dummy test cell. I now needed to work on rendering the data in its corresponding cell.

I thought a while on how to do this, because it isn’t a view seen anywhere else in the application yet. What needed to be displayed was both the Dish and Wine descriptions; it needed to show the entire recommendation restriction in a single cell. I decided to go forward by changing the UITableViewCellRecommendation to add a new initialization that would work with the new data format as the input. I expected this to need a mode to be made to keep track of what the cell was being used for, a new init function, and some new logic in the various functions related to cell generation and height calculation in both the cell and view classes.

My initial attempts crashed, and a series of tests using different numbers of set rows to check my logic for distinguishing between the dish and wine description pair counts revealed a few issues that needed to be fixed, similar to the ones I encountered when doing the logic between the necessities/exclusions before.

The result showed all of the data, but as you can see in the screenshot needed a space in between the two descriptions to make it clear where the two descriptions are and still needed all the height changes.

I made the space by creating a dummy cell in between the two, updating the logic once again to account for the extra cell in between. This brought up some more issues with displaying it, the most prominent being the apparent loss of the bottom pair for the wine description. After a whole bunch of tests on the completely wrong things, I eventually suspected that it might not be the logic or cell counts that were off, but the height settings. Extending the height manually confirmed this, and it was simple to simply fiddle with the height calculation functions to add the needed space to account for the dummy spot.

With that, all the buttons in the Preferences UI function and return the data correctly, which is displayed fine in the main view. There are just two major issues remaining. The first is that the preferences are not editable once entered, and I need to change that so that they will be selectable and will call up their respective views with the existing data so the user can change it accordingly and will update the view correctly on return. The second is that the preferences data is still not persistent; when the user exists out of the Preferences UI all the data is lost…clearly not something we want from a user preferences function! As far as the UI itself, however, it is pretty complete.

So I had hoped to finish implementing all of the functionality for each of the buttons and get all the data to return correctly to the main view. As it turns out, I was able to do this for all of the Food/Wine Restrictions, but did not have time to begin work on returning the data from the Recommendation Restrictions, nor fixing the UI for the views being called (will talk more on that later).

The first thing that I did was to simply connect each of the buttons to their respective selection windows. To reiterate, each of the Food/Wine buttons call up the description generation view where a list of properties is made, whereas the Recommendation button calls up a recommendation view where the same process is done, but on both a dish and a wine before returning back to this main view.

After some fiddling I got them all to return without crashing, although the content being returned was not implemented yet. The initial problem with the crashing was due to me dismissing the new views incorrectly, which was easily fixed. At this time I also realized that there are several things I need to fix with the Recommendation restriction UI, such as changing the title and removing the commit button since it’ll return using the back tab instead of that. I’ll get into more detail on plans for this section at the end, since I did not work much on it today.

I worked slowly on figuring out how the data is being passed around and in what form in various other functions to try to grasp how to do it here. In particular, I looked through the MakeRecommendationViewController and DescriptionGenerator classes.

I began by simply adding test buttons each time, just to make sure that I was indeed adding data and returning correctly. I ran into some crashes before I started adding the cells since it was calling the cell at indexPath but I had no code there to return a cell. During this change I realized that I needed some sort of modes to distinguish where to add it, due to the way I needed to handle the indexPath as a combination of both the necessity and exclusion counts. I did this by adding an enumeration to the class for easy reading and the addition of the mode variable.

After adding the modes to make sure I was accessing the right data at the right places to prevent crashes, I was able to make the test cells. I also noted at this point that although I was setting them to be read-only right now and therefore unselectable, I would need to make them selectable so that the user could change their previously set preferences. I don’t want to work on this until I complete the entire basic functionality, however, so I will just keep this in mind.

Another issue is how you can see that at this point all the rows had that green + next to it, which I only wanted for the actual Add buttons. My initial attempt to change this by changing the cell initialization call did not work, so I went to the documentation. After looking through this, I tried to change the imageView thinking that it was an image being set somewhere, but trying to remove it that way did nothing. I eventually realized after looking around some more that it was not an image but the UITableViewCellEditingStyle. This green + is due to the style being set to insert, whereas I wanted none. Once I figured this out, it was simple to fix by putting in a switch statement with some logic governing where to use each style.

I continued to trying to display the actual data. My initial attempts failed despite me trying several kinds of formats for the data I thought was being returned. After going back and reexamining how the data was being passed and my calls to try to get the data into the cell I realized that I had actually gotten the correct format the first time, but had simply messed up the call.

It was saving the data as a NSArray of a Pair like I thought, but I was accessing it by calling the entire data NSArray in the main view, where I needed to call the index instead, which would get the correct NSArray. In other words, in the case of _foodNecessities, it is a NSArray of NSArrays of Pairs, and I was calling the first index right, but did not go the extra layer that I needed to access the right piece of data.

Just a reminder that all this work was aimed at the Food/Wine restrictions, not the Recommendation one, which will work differently and will have a completely different data format and different rendering in the UI.

After getting the data to display correctly, I realized that it was not resizing the cell correctly, simply showing it as the default height. I also realized that there were issues because I forgot that I needed additional calculations within my logic when figuring out which index of the exclusion to access for that, since the indexPath would count both the necessity count and the exclusion count since it is the index for the entire section. After a bit of testing, fiddling, and retesting, this was fixed with little trouble.

I fixed the size by implementing the height calculation function, which was previously left to the default, and adding a switch statement with the usual logic to call the correct height function for each cell.

This should finish the UI part of the Food/Wine restrictions, apart from needing to change the cells from read-only to editable in a way that will call the description generator with the correct data and edit the cell on return. This will have to wait until I finish getting the UI to work for the Recommendation restriction, however, which I hope to finish tomorrow…technically today now.

For the Recommendation UI, I will need to change it so that it doesn’t have the commit button, since it just needs to return the data via the left button returning it to the main view. Following this, of course, is the need to get the view to return the data upon this return, and the big task of figuring out a way to display this data. Since it will likely be the large recommendation data being returned with both the dish and wine data, it might need me to define a new cell type. I’ll have to look into this more when I actually begin work on it tomorrow. I also need to change the title, since it is not being called as the Make a Recommendation function, but rather just to get the recommendation format and return the data to the view, the identical reason why the Commit button is unneeded.