{"id":127,"date":"2022-03-15T21:53:09","date_gmt":"2022-03-15T21:53:09","guid":{"rendered":"https:\/\/blogs.oregonstate.edu\/davidkaff\/?p=127"},"modified":"2022-03-18T09:53:28","modified_gmt":"2022-03-18T09:53:28","slug":"grocc-v1_0_2","status":"publish","type":"post","link":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/","title":{"rendered":"&#8216;grocc&#8217; v1.0.2"},"content":{"rendered":"\n<p>This term I had the opportunity to work on a personal project for college credit through the independent study CS 406 &#8220;Projects&#8221; course. I chose to work on my grocery and recipe utility iOS app &#8216;<a href=\"https:\/\/apps.apple.com\/app\/grocc\/id1585242020\" target=\"_blank\" rel=\"noreferrer noopener\">grocc<\/a>&#8216; which allows users to quickly translate a meal plan into a grocery list.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Overview<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>My goals for this term were to:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ol class=\"wp-block-list\"><li>Complete development of recipe ingredient keeping functionality.<\/li><li>Integrate the grocery list export functionality with one or more to-do list apps.<\/li><li>Successfully publish the app to either the Google Play Store and Apple App Store.<\/li><li>Complete a write-up on the completed app for my tech blog showcasing my development skills.<\/li><\/ol>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>What I accomplished this term was:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>Complete development of core feature set, including recipe keeping and list checking.<\/li><li>Itegrated the grocery list export functionality with Todoist&#8217;s REST v2 API (based on manual API token entry).<\/li><li>Successfully published the app to the Apple iOS App Store.<\/li><li>Posting this write-up on the work I completed and the finished product!<\/li><\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Finished Product<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>&#8216;grocc&#8217; can be <a href=\"https:\/\/apps.apple.com\/app\/grocc\/id1585242020\" target=\"_blank\" rel=\"noreferrer noopener\">viewed in the App Store<\/a>.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>&#8216;grocc&#8217; is a recipe and grocery utility app for iOS which allows users to quickly translate a meal plan into a grocery list.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Users can enter their recipes\u2019 ingredient lists into the app, and modify or delete previously entered ingredients and recipes.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"132\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\" alt=\"\" class=\"wp-image-132\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"133\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-544x1024.png\" alt=\"\" class=\"wp-image-133\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-recipe-1.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"134\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-544x1024.png\" alt=\"\" class=\"wp-image-134\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/edit-ingredient-1.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n<\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Users can select recipes to generate a grocery list, including multiple selections of a given recipe to ensure the correct quantities of ingredients are purchased.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-544x1024.png\" alt=\"\" class=\"wp-image-135\" width=\"272\" height=\"512\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/selection.png 1128w\" sizes=\"auto, (max-width: 272px) 100vw, 272px\" \/><\/figure><\/div>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Once users have selected their desired recipes, they are presented with a checklist and are invited to check their current pantry to ensure they actually need to purchase ingredients as groceries.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"136\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-544x1024.png\" alt=\"\" class=\"wp-image-136\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist1.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"137\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-544x1024.png\" alt=\"\" class=\"wp-image-137\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/ingCheck.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"138\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-544x1024.png\" alt=\"\" class=\"wp-image-138\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/checklist2.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n<\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>After checking their list, users can export their grocery list to Todoist.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>When attempting to export their grocery list to Todoist, if the user has not yet entered their Todoist API token a form is displayed which accepts this token. After entering their Todoist API token, users can select the project or project section within their Todoist library which they want to export their grocery list to. Both of these features can also be accessed from the main menu of the app.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"139\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-544x1024.png\" alt=\"\" class=\"wp-image-139\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/exporting.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"140\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-544x1024.png\" alt=\"\" class=\"wp-image-140\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/todoist-api-token-entered.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"544\" height=\"1024\" data-id=\"141\" src=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-544x1024.png\" alt=\"\" class=\"wp-image-141\" srcset=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-544x1024.png 544w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-159x300.png 159w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-768x1446.png 768w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-816x1536.png 816w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location-1088x2048.png 1088w, https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/change-export-location.png 1128w\" sizes=\"auto, (max-width: 544px) 100vw, 544px\" \/><\/figure>\n<\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Features Implemented<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>The features I implemented during the course include: <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>Main Menu for app<\/li><li>Multiple selection of recipes when generating grocery list<\/li><li>Help dialog\/FAQ for \u2018Generate Grocery List\u2019 page<\/li><li>Integration Test Suite<\/li><li>&#8220;Checklist Hopping&#8221;<\/li><li>Quantity+Measure form as dialog (not page)<\/li><li>Show Q+M form on new ingredient completion<\/li><li>Overload back button to minimize data loss<\/li><li>Edit\/Delete ingredients in new and previously entered recipes<\/li><li>Delete recipe<\/li><li>Manage Todoist API Token<\/li><li>Select Todoist Export Location<\/li><\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Tech Stack<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>The primary technology that I utilized for the development of \u201cgrocc\u201d was Flutter. Flutter is a cross-platform mobile application framework which is written in the Dart programming language. Both Dart and Flutter are created by Google, and are open-source.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I used several Flutter packages to offload development tasks. Packages I used include: <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>\u201cgoogle_fonts\u201d to load fonts for the app<\/li><li>\u201csqflite\u201d to implement two SQLite databases on the host device to store recipe and ingredient data<\/li><li>\u201cshared_preferences\u201d to gain access to persistent key:value data storage on the host device, which I used to persist the Todoist API token and export location after entered by the user<\/li><li>\u201cuuid\u201d to generate uuid\u2019s fro Todoist API requests. <\/li><li>\u201cintegration_test\u201d to enable the running of integration tests of the app<\/li><\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>To deploy the app, I utilized Xcode and the Apple Developer program to upload an archive of the app, and have published the iOS version of the app to the Apple iOS App Store.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Successes<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I accomplished all of the tasks in my initial plan, as well as several more (including development of a test suite and development of the \u201cmodify ingredient list of recipe\u201d feature) which were added to subsequent versions of the plan.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>In this project I succeeded at:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<ul class=\"wp-block-list\"><li>adapting my plan to accommodate development delays, <\/li><li>discovering critical features which were overlooked during initial planning, <\/li><li>learning to create and run integration tests for Flutter apps, <\/li><li>discovering and documenting potential features, <\/li><li>not letting said discoveries derail active development according to the development plan, <\/li><li>making regular status updates about project development, and <\/li><li>completing the development of a tool I now use on a weekly basis.<\/li><\/ul>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Challenges<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>In this project I faced the following challenges:<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Missing a Plan for Testing<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>In my initial project plan I failed to plan for testing of my app. Thanks to feedback on my initial submission, I created a plan to develop integration tests of the main features of my app and revised my project plan to include the associated tasks. The current version of my app is now well tested with integration tests.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Discovering Potential Features, and Sticking to Plan<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Throughout development, I faced uncertainty in regards to whether or not I should pursue the development of potential features which I discovered while developing previously planned features. In most cases, I opted to instead only take careful notes about the newly discovered features. <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>However, I did choose to pursue the development of the \u201cedit\/delete ingredient list in recipe\u201d feature, which I rediscovered after beginning development this term. I felt that as opposed to many of the other potential features that I discovered such as \u201chydrate state of ingredients checked when adding recipes after checking some ingredients\u201d, \u201cexporting quantities and measures needed for an ingredient as \u2018secondary item text\u2019 to Todoist\u201d, and \u201cadd ad-hoc grocery item in checklist\u201d, these ingredient modification features seemed essential to an initial public release. <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>I believe I made the correct decision to pursue the development of those features, but to defer the development of other potential features in order to stay on track with my plan.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Bug Fix: Exporting Unwanted Items<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>During development, I discovered the following bug: When checking an ingredient list to generate a grocery list, if an ingredient was initially tagged by a user as \u201cneed\u201d (and thus added to the grocery list) but was later retagged as \u201chave\u201d (which should remove it from the grocery list), this item would still be exported to Todoist as an item on the grocery list. I tracked down this bug, and the cause turned out to be that while my code was removing the ingredient from the list which informs the display of the ingredient on the checklist, my code was not removing the ingredient from the \u201cgrocery list\u201d list. To remedy this, I rewrote the \u201caddToList()\u201d function to be a \u201csetCheckListItem()\u201d function which accepts a bool named \u201cadding\u201d to indicate whether the ingredient in question is being added to or removed from (if applicable) the grocery list.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Bug Fix: Multiple Appearance of Single Checklist Item<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>During development, I discovered the following bug: When generating the checklist of ingredients, if an ingredient was in multiple recipes it would sometimes appear more than once on the checklist. This was a bug because I believed my code was sorting all of the like ingredients together and parsing grouped like ingredients into a single checklist item. As it turns out, my sorting algorithm was correctly grouping the ingredients by \u201cname\u201d, but a subsequent sort by \u201ctype\u201d was behaving differently than I had anticipated and breaking up some \u201cname\u201d groups of ingredients. To remedy this, I changed my sorting procedure to first create separate lists of the ingredients based on their \u201ctype\u201d, then sorting each list of \u201ctype\u201dd ingredients by \u201cname\u201d, and finally returning the concatenation of all the sorted \u201ctype\u201dd lists of ingredients as a single list.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Bug Fix: Recipe Selection State Corrupted<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>During development, I discovered the following bug: When returning from a checklist to add or remove a recipe and regenerate the checklist, the state of which recipes had been selected was corrupted and or missing. The problem turned out to be that the function I had written to generate the checklist from the selected recipes was consuming the data which indicated the user\u2019s recipe selections. The solution was to first copy this data then consume only the copied data in the function, leaving the original selection data unchanged in the state of the app.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">App Store Submission Hangups<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>When I first submitted the app for review to the iOS App Store, my submission was rejected. This was frustrating as the app had been previously approved for distribution via TestFlight, and I was uncertain as to the differences between the two approval processes. <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>The problems with my submission turned out to be fairly simple \u2014 the screenshots I had submitted included the Flutter \u201cdebug\u201d banner. This goes against an App Store guideline which prohibits unnecessary references to the development process. Additionally the reviewer wanted answers to a few basic questions about the purpose and target audience of the app (as this was my first submission to the App Store for this app). <\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>To resolve these issues I answered the questions, used an image editor to cover up the \u201cdebug\u201d banner in my app screenshots, and resubmitted for review. This second submission was approved, and now my app is available on the App Store.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h4 class=\"wp-block-heading\">Feature Discovery: Informing User of Token Validity<\/h4>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>When I was planning to work on the integration test for the \u201cmanage Todoist credentials\u201d feature\/menu, I realized that users were not receiving sufficient feedback to determine whether the API token they had entered would allow them to export their grocery lists. This led to a feature discovery \u2014 \u201ctoken trial dialogs\u201d which inform the user of the validity of the API token that they entered.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>This term I completed an independent study course in which I developed features and tests for a grocery list app in preparation for its initial public release. I adapted to various challenges that cropped up during development, regularly reported on my progress, and ultimately published the app to the iOS App Store.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\" \/>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n","protected":false},"excerpt":{"rendered":"<p>This term I had the opportunity to work on a personal project for college credit through the independent study CS 406 &#8220;Projects&#8221; course. I chose to work on my grocery and recipe utility iOS app &#8216;grocc&#8216; which allows users to quickly translate a meal plan into a grocery list. Overview My goals for this term&hellip; <a class=\"more-link\" href=\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\">Continue reading <span class=\"screen-reader-text\">&#8216;grocc&#8217; v1.0.2<\/span><\/a><\/p>\n","protected":false},"author":11555,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-127","post","type-post","status-publish","format-standard","hentry","category-uncategorized","entry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>&#039;grocc&#039; v1.0.2 - David Kaff<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"&#039;grocc&#039; v1.0.2 - David Kaff\" \/>\n<meta property=\"og:description\" content=\"This term I had the opportunity to work on a personal project for college credit through the independent study CS 406 &#8220;Projects&#8221; course. I chose to work on my grocery and recipe utility iOS app &#8216;grocc&#8216; which allows users to quickly translate a meal plan into a grocery list. Overview My goals for this term&hellip; Continue reading &#8216;grocc&#8217; v1.0.2\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\" \/>\n<meta property=\"og:site_name\" content=\"David Kaff\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-15T21:53:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-03-18T09:53:28+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\" \/>\n<meta name=\"author\" content=\"David Kaff\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"David Kaff\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\"},\"author\":{\"name\":\"David Kaff\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353\"},\"headline\":\"&#8216;grocc&#8217; v1.0.2\",\"datePublished\":\"2022-03-15T21:53:09+00:00\",\"dateModified\":\"2022-03-18T09:53:28+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\"},\"wordCount\":1632,\"commentCount\":0,\"image\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\",\"url\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\",\"name\":\"'grocc' v1.0.2 - David Kaff\",\"isPartOf\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\",\"datePublished\":\"2022-03-15T21:53:09+00:00\",\"dateModified\":\"2022-03-18T09:53:28+00:00\",\"author\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353\"},\"breadcrumb\":{\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage\",\"url\":\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\",\"contentUrl\":\"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"&#8216;grocc&#8217; v1.0.2\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/#website\",\"url\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/\",\"name\":\"David Kaff\",\"description\":\"fledgling software engineer\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353\",\"name\":\"David Kaff\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g\",\"caption\":\"David Kaff\"},\"url\":\"https:\/\/blogs.oregonstate.edu\/davidkaff\/author\/kaffs\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"'grocc' v1.0.2 - David Kaff","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/","og_locale":"en_US","og_type":"article","og_title":"'grocc' v1.0.2 - David Kaff","og_description":"This term I had the opportunity to work on a personal project for college credit through the independent study CS 406 &#8220;Projects&#8221; course. I chose to work on my grocery and recipe utility iOS app &#8216;grocc&#8216; which allows users to quickly translate a meal plan into a grocery list. Overview My goals for this term&hellip; Continue reading &#8216;grocc&#8217; v1.0.2","og_url":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/","og_site_name":"David Kaff","article_published_time":"2022-03-15T21:53:09+00:00","article_modified_time":"2022-03-18T09:53:28+00:00","og_image":[{"url":"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png","type":"","width":"","height":""}],"author":"David Kaff","twitter_card":"summary_large_image","twitter_misc":{"Written by":"David Kaff","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#article","isPartOf":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/"},"author":{"name":"David Kaff","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353"},"headline":"&#8216;grocc&#8217; v1.0.2","datePublished":"2022-03-15T21:53:09+00:00","dateModified":"2022-03-18T09:53:28+00:00","mainEntityOfPage":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/"},"wordCount":1632,"commentCount":0,"image":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage"},"thumbnailUrl":"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/","url":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/","name":"'grocc' v1.0.2 - David Kaff","isPartOf":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage"},"image":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage"},"thumbnailUrl":"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png","datePublished":"2022-03-15T21:53:09+00:00","dateModified":"2022-03-18T09:53:28+00:00","author":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353"},"breadcrumb":{"@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#primaryimage","url":"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png","contentUrl":"https:\/\/osu-wams-blogs-uploads.s3.amazonaws.com\/blogs.dir\/4766\/files\/2022\/03\/recipe-list-1-544x1024.png"},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/2022\/03\/15\/grocc-v1_0_2\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blogs.oregonstate.edu\/davidkaff\/"},{"@type":"ListItem","position":2,"name":"&#8216;grocc&#8217; v1.0.2"}]},{"@type":"WebSite","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/#website","url":"https:\/\/blogs.oregonstate.edu\/davidkaff\/","name":"David Kaff","description":"fledgling software engineer","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blogs.oregonstate.edu\/davidkaff\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blogs.oregonstate.edu\/davidkaff\/#\/schema\/person\/0d93f65fc3b5939cc721d30965e4f353","name":"David Kaff","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/feece6673b6f1dbb8044d124b27ad93d9c837024b690380c1f3a9cb710d0a470?s=96&d=mm&r=g","caption":"David Kaff"},"url":"https:\/\/blogs.oregonstate.edu\/davidkaff\/author\/kaffs\/"}]}},"_links":{"self":[{"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/posts\/127","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/users\/11555"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/comments?post=127"}],"version-history":[{"count":8,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/posts\/127\/revisions"}],"predecessor-version":[{"id":183,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/posts\/127\/revisions\/183"}],"wp:attachment":[{"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/media?parent=127"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/categories?post=127"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.oregonstate.edu\/davidkaff\/wp-json\/wp\/v2\/tags?post=127"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}