{"action":"created","comment":{"url":"https://api.github.com/repos/mitodl/micromasters/pulls/comments/90956561","pull_request_review_id":11470137,"id":90956561,"diff_hunk":"@@ -57,148 +47,164 @@ export function programValidation(_: Profile, ui: UIState): ValidationErrors {\n   return errors;\n }\n \n+/*\n+ * Profile Image Validator\n+ */\n export const profileImageValidation = (profile: Profile) => (\n   profile.image ? {} : { image: 'Please upload a profile image' }\n );\n \n-export function personalValidation(profile: Profile): ValidationErrors {\n-  let requiredFields = [\n-    ['first_name'],\n-    ['last_name'],\n-    ['preferred_name'],\n-    ['gender'],\n-    ['preferred_language'],\n-    ['city'],\n-    ['state_or_territory'],\n-    ['country'],\n-    ['nationality'],\n-    ['birth_country'],\n-    ['date_of_birth'],\n-  ];\n-  let validationMessages = {\n-    'first_name': \"Given name is required\",\n-    'last_name': \"Family name is required\",\n-    'preferred_name': \"Nickname / Preferred name is required\",\n-    'gender': \"Gender is required\",\n-    'preferred_language': \"Preferred language is required\",\n-    'city': \"City is required\",\n-    'state_or_territory': 'State or Territory is required',\n-    'country': \"Country is required\",\n-    'birth_country': \"Country is required\",\n-    'nationality': \"Nationality is required\",\n-    'date_of_birth': 'Please enter a valid date of birth'\n-  };\n-  let errors = checkFieldPresence(profile, requiredFields, validationMessages);\n+/*\n+ * Personal Validation\n+ */\n+const personalMessages = {\n+  'first_name': \"Given name is required\",\n+  'last_name': \"Family name is required\",\n+  'preferred_name': \"Nickname / Preferred name is required\",\n+  'gender': \"Gender is required\",\n+  'preferred_language': \"Preferred language is required\",\n+  'city': \"City is required\",\n+  'state_or_territory': 'State or Territory is required',\n+  'country': \"Country is required\",\n+  'birth_country': \"Country is required\",\n+  'nationality': \"Nationality is required\",\n+  'date_of_birth': 'Please enter a valid date of birth'\n+};\n+\n+export const personalValidation = (profile: Profile) => {\n+  let errors = findErrors(profile, R.keys(personalMessages), personalMessages);\n   if (!moment(profile.date_of_birth).isBefore(moment(), 'day')) {\n-    // birthdays must be before today\n-    errors.date_of_birth = validationMessages.date_of_birth;\n+    errors.date_of_birth = personalMessages.date_of_birth;\n   }\n   return errors;\n-}\n+};\n \n-export function educationValidation(profile: Profile): ValidationErrors {\n-  let messages = {\n-    'degree_name': 'Degree level is required',\n-    'graduation_date': 'Please enter a valid graduation date',\n-    'field_of_study': 'Field of study is required',\n-    'online_degree': 'Online Degree is required',\n-    'school_name': 'School name is required',\n-    'school_city': 'City is required',\n-    'school_state_or_territory': 'State is required',\n-    'school_country': 'Country is required'\n-  };\n-  let nestedKeys = [\n-    'degree_name',\n-    'graduation_date',\n-    'field_of_study',\n-    'online_degree',\n-    'school_name',\n-    'school_city',\n-    'school_state_or_territory',\n-    'school_country'\n-  ];\n-  if (!_.isEmpty(profile.education)) {\n-    let requiredFields = handleNestedValidation(profile, nestedKeys, 'education');\n-    requiredFields = requiredFields.filter(([, index, key]) =>\n-      // don't require field of study for high school students\n-      !(key === 'field_of_study' && _.get(profile, ['education', index, 'degree_name']) === HIGH_SCHOOL)\n-    );\n-    return checkFieldPresence(profile, requiredFields, messages);\n-  } else {\n+/*\n+ * Helper for nested validators\n+ * key is the top level key on profile\n+ * findErrors is a function which return an error object\n+ * profile is, well, a profile\n+ */\n+const nestedValidator = R.curry((key, findErrors, profile) => {\n+  if (R.isEmpty(profile[key]) || R.isNil(profile[key])) {\n     return {};\n   }\n-}\n-\n-export type WorkEntry = [string, WorkHistoryEntry];\n-export function employmentValidation(profile: Profile): ValidationErrors {\n-  let messages = {\n-    'position': 'Position is required',\n-    'industry': 'Industry is required',\n-    'company_name': 'Name of Employer is required',\n-    'start_date': 'Please enter a valid start date',\n-    'city': 'City is required',\n-    'country': 'Country is required',\n-    'state_or_territory': 'State or Territory is required',\n-  };\n-  let nestedKeys = [\n-    'position',\n-    'industry',\n-    'company_name',\n-    'start_date',\n-    'city',\n-    'country',\n-    'state_or_territory',\n-  ];\n-  if (!_.isEmpty(profile.work_history)) {\n-    let requiredFields = handleNestedValidation(profile, nestedKeys, 'work_history');\n-    let errors = checkFieldPresence(profile, requiredFields, messages);\n-\n-    profile.work_history.forEach((workHistory, index) => {\n-      if (!isNilOrEmptyString(workHistory.end_date) &&\n-        moment(workHistory.end_date).isBefore(workHistory.start_date, 'month')) {\n-        _.set(errors, ['work_history', String(index), 'end_date'], \"End date cannot be before start date\");\n-      }\n-      if (!isNilOrEmptyString(workHistory.end_date) &&\n-        moment(workHistory.end_date).isAfter(moment(), 'month')) {\n-        _.set(errors, ['work_history', String(index), 'end_date'], 'End date cannot be in the future');\n-      }\n-      let editIsEmpty = _.isEmpty(workHistory.end_date_edit) || (\n-        workHistory.end_date_edit !== undefined &&\n-        isNilOrEmptyString(workHistory.end_date_edit.year) &&\n-          isNilOrEmptyString(workHistory.end_date_edit.month)\n-      );\n-      if (isNilOrEmptyString(workHistory.end_date) && !editIsEmpty) {\n-        _.set(errors, ['work_history', String(index), 'end_date'], \"Please enter a valid end date or leave it blank\");\n-      }\n-    });\n-\n-    return errors;\n-  } else {\n+  let errors = findErrors(profile[key]);\n+  if ( R.equals(errors, R.repeat({}, errors.length)) ) {\n     return {};\n   }\n-}\n+  return { [key]: errors };\n+});\n \n-export function privacyValidation(profile: Profile): ValidationErrors {\n-  let requiredFields = [\n-    ['account_privacy']\n-  ];\n-  let messages = {\n-    'account_privacy': 'Privacy level is required'\n-  };\n-  return checkFieldPresence(profile, requiredFields, messages);\n-}\n+/*\n+ * Education Validation\n+ */\n+const educationMessages = {\n+  'degree_name': 'Degree level is required',\n+  'graduation_date': 'Please enter a valid graduation date',\n+  'field_of_study': 'Field of study is required',\n+  'online_degree': 'Online Degree is required',\n+  'school_name': 'School name is required',\n+  'school_city': 'City is required',\n+  'school_state_or_territory': 'State is required',\n+  'school_country': 'Country is required'\n+};\n+\n+const isHighSchool = R.compose(\n+  R.equals(HIGH_SCHOOL), R.prop('degree_name')\n+);\n+\n+const excludeFieldOfStudy = R.filter(\n+  R.compose(R.not, R.equals('field_of_study'))\n+);\n+\n+const educationKeys = R.ifElse(\n+  isHighSchool, R.compose(excludeFieldOfStudy, R.keys), R.keys\n+);\n+\n+const educationErrors = R.map(entry => (\n+  findErrors(entry, educationKeys(entry), educationMessages)\n+));\n+\n+export const educationValidation = nestedValidator('education', educationErrors);\n+\n+/*\n+ * Work History Validation\n+ */\n+const workMessages = {\n+  'position': 'Position is required',\n+  'industry': 'Industry is required',\n+  'company_name': 'Name of Employer is required',\n+  'start_date': 'Please enter a valid start date',\n+  'city': 'City is required',\n+  'country': 'Country is required',\n+  'state_or_territory': 'State or Territory is required',\n+};\n+\n+// functions to perform extra checks\n+// must be binary functions taking (entry, errors)\n+// where entry is a work history entry\n+// and errors is the output of workHistoryErrors\n+// and returning errors U newErrors","path":"static/js/lib/validation/profile.js","position":271,"original_position":271,"commit_id":"a560ce670ef5ee7961319dc89b9dfe928ee92b20","original_commit_id":"c7a63457303e66d1d49e135cddbc09883cf0c45f","user":{"login":"aliceriot","id":6207644,"avatar_url":"https://avatars.githubusercontent.com/u/6207644?v=3","gravatar_id":"","url":"https://api.github.com/users/aliceriot","html_url":"https://github.com/aliceriot","followers_url":"https://api.github.com/users/aliceriot/followers","following_url":"https://api.github.com/users/aliceriot/following{/other_user}","gists_url":"https://api.github.com/users/aliceriot/gists{/gist_id}","starred_url":"https://api.github.com/users/aliceriot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aliceriot/subscriptions","organizations_url":"https://api.github.com/users/aliceriot/orgs","repos_url":"https://api.github.com/users/aliceriot/repos","events_url":"https://api.github.com/users/aliceriot/events{/privacy}","received_events_url":"https://api.github.com/users/aliceriot/received_events","type":"User","site_admin":false},"body":"(well, sort of the set union, objects aren't really sets at all, haha)","created_at":"2016-12-05T21:14:24Z","updated_at":"2016-12-05T21:14:24Z","html_url":"https://github.com/mitodl/micromasters/pull/1969#discussion_r90956561","pull_request_url":"https://api.github.com/repos/mitodl/micromasters/pulls/1969","_links":{"self":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/comments/90956561"},"html":{"href":"https://github.com/mitodl/micromasters/pull/1969#discussion_r90956561"},"pull_request":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/1969"}}},"pull_request":{"url":"https://api.github.com/repos/mitodl/micromasters/pulls/1969","id":96574929,"html_url":"https://github.com/mitodl/micromasters/pull/1969","diff_url":"https://github.com/mitodl/micromasters/pull/1969.diff","patch_url":"https://github.com/mitodl/micromasters/pull/1969.patch","issue_url":"https://api.github.com/repos/mitodl/micromasters/issues/1969","number":1969,"state":"open","locked":false,"title":"refactor profile validation code","user":{"login":"aliceriot","id":6207644,"avatar_url":"https://avatars.githubusercontent.com/u/6207644?v=3","gravatar_id":"","url":"https://api.github.com/users/aliceriot","html_url":"https://github.com/aliceriot","followers_url":"https://api.github.com/users/aliceriot/followers","following_url":"https://api.github.com/users/aliceriot/following{/other_user}","gists_url":"https://api.github.com/users/aliceriot/gists{/gist_id}","starred_url":"https://api.github.com/users/aliceriot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aliceriot/subscriptions","organizations_url":"https://api.github.com/users/aliceriot/orgs","repos_url":"https://api.github.com/users/aliceriot/repos","events_url":"https://api.github.com/users/aliceriot/events{/privacy}","received_events_url":"https://api.github.com/users/aliceriot/received_events","type":"User","site_admin":false},"body":"#### What are the relevant tickets?\r\n\r\ncloses #1951 \r\n\r\n#### What's this PR do?\r\n\r\nThis rewrites the profile validation code in a more functional style, which lets us have a greater degree of code reuse and, hopefully, readability.\r\n\r\n#### Where should the reviewer start?\r\n\r\nRead over the changes, and make sure they make sense.\r\n\r\n#### How should this be manually tested?\r\n\r\nWell, I think just make sure there aren't any regressions in the validation UX, when filling out the profile.","created_at":"2016-12-05T17:38:52Z","updated_at":"2016-12-05T21:14:24Z","closed_at":null,"merged_at":null,"merge_commit_sha":"6ec155000a5a19c1eca7074589ddf5eae044c956","assignee":{"login":"noisecapella","id":863262,"avatar_url":"https://avatars.githubusercontent.com/u/863262?v=3","gravatar_id":"","url":"https://api.github.com/users/noisecapella","html_url":"https://github.com/noisecapella","followers_url":"https://api.github.com/users/noisecapella/followers","following_url":"https://api.github.com/users/noisecapella/following{/other_user}","gists_url":"https://api.github.com/users/noisecapella/gists{/gist_id}","starred_url":"https://api.github.com/users/noisecapella/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/noisecapella/subscriptions","organizations_url":"https://api.github.com/users/noisecapella/orgs","repos_url":"https://api.github.com/users/noisecapella/repos","events_url":"https://api.github.com/users/noisecapella/events{/privacy}","received_events_url":"https://api.github.com/users/noisecapella/received_events","type":"User","site_admin":false},"assignees":[{"login":"noisecapella","id":863262,"avatar_url":"https://avatars.githubusercontent.com/u/863262?v=3","gravatar_id":"","url":"https://api.github.com/users/noisecapella","html_url":"https://github.com/noisecapella","followers_url":"https://api.github.com/users/noisecapella/followers","following_url":"https://api.github.com/users/noisecapella/following{/other_user}","gists_url":"https://api.github.com/users/noisecapella/gists{/gist_id}","starred_url":"https://api.github.com/users/noisecapella/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/noisecapella/subscriptions","organizations_url":"https://api.github.com/users/noisecapella/orgs","repos_url":"https://api.github.com/users/noisecapella/repos","events_url":"https://api.github.com/users/noisecapella/events{/privacy}","received_events_url":"https://api.github.com/users/noisecapella/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/mitodl/micromasters/pulls/1969/commits","review_comments_url":"https://api.github.com/repos/mitodl/micromasters/pulls/1969/comments","review_comment_url":"https://api.github.com/repos/mitodl/micromasters/pulls/comments{/number}","comments_url":"https://api.github.com/repos/mitodl/micromasters/issues/1969/comments","statuses_url":"https://api.github.com/repos/mitodl/micromasters/statuses/a560ce670ef5ee7961319dc89b9dfe928ee92b20","head":{"label":"mitodl:ap/refactor-profile-validation","ref":"ap/refactor-profile-validation","sha":"a560ce670ef5ee7961319dc89b9dfe928ee92b20","user":{"login":"mitodl","id":7918516,"avatar_url":"https://avatars.githubusercontent.com/u/7918516?v=3","gravatar_id":"","url":"https://api.github.com/users/mitodl","html_url":"https://github.com/mitodl","followers_url":"https://api.github.com/users/mitodl/followers","following_url":"https://api.github.com/users/mitodl/following{/other_user}","gists_url":"https://api.github.com/users/mitodl/gists{/gist_id}","starred_url":"https://api.github.com/users/mitodl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitodl/subscriptions","organizations_url":"https://api.github.com/users/mitodl/orgs","repos_url":"https://api.github.com/users/mitodl/repos","events_url":"https://api.github.com/users/mitodl/events{/privacy}","received_events_url":"https://api.github.com/users/mitodl/received_events","type":"Organization","site_admin":false},"repo":{"id":52919185,"name":"micromasters","full_name":"mitodl/micromasters","owner":{"login":"mitodl","id":7918516,"avatar_url":"https://avatars.githubusercontent.com/u/7918516?v=3","gravatar_id":"","url":"https://api.github.com/users/mitodl","html_url":"https://github.com/mitodl","followers_url":"https://api.github.com/users/mitodl/followers","following_url":"https://api.github.com/users/mitodl/following{/other_user}","gists_url":"https://api.github.com/users/mitodl/gists{/gist_id}","starred_url":"https://api.github.com/users/mitodl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitodl/subscriptions","organizations_url":"https://api.github.com/users/mitodl/orgs","repos_url":"https://api.github.com/users/mitodl/repos","events_url":"https://api.github.com/users/mitodl/events{/privacy}","received_events_url":"https://api.github.com/users/mitodl/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mitodl/micromasters","description":"Portal for learners and course teams to access MITx micromasters programs: https://goo.gl/tsUTSO","fork":false,"url":"https://api.github.com/repos/mitodl/micromasters","forks_url":"https://api.github.com/repos/mitodl/micromasters/forks","keys_url":"https://api.github.com/repos/mitodl/micromasters/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mitodl/micromasters/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mitodl/micromasters/teams","hooks_url":"https://api.github.com/repos/mitodl/micromasters/hooks","issue_events_url":"https://api.github.com/repos/mitodl/micromasters/issues/events{/number}","events_url":"https://api.github.com/repos/mitodl/micromasters/events","assignees_url":"https://api.github.com/repos/mitodl/micromasters/assignees{/user}","branches_url":"https://api.github.com/repos/mitodl/micromasters/branches{/branch}","tags_url":"https://api.github.com/repos/mitodl/micromasters/tags","blobs_url":"https://api.github.com/repos/mitodl/micromasters/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mitodl/micromasters/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mitodl/micromasters/git/refs{/sha}","trees_url":"https://api.github.com/repos/mitodl/micromasters/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mitodl/micromasters/statuses/{sha}","languages_url":"https://api.github.com/repos/mitodl/micromasters/languages","stargazers_url":"https://api.github.com/repos/mitodl/micromasters/stargazers","contributors_url":"https://api.github.com/repos/mitodl/micromasters/contributors","subscribers_url":"https://api.github.com/repos/mitodl/micromasters/subscribers","subscription_url":"https://api.github.com/repos/mitodl/micromasters/subscription","commits_url":"https://api.github.com/repos/mitodl/micromasters/commits{/sha}","git_commits_url":"https://api.github.com/repos/mitodl/micromasters/git/commits{/sha}","comments_url":"https://api.github.com/repos/mitodl/micromasters/comments{/number}","issue_comment_url":"https://api.github.com/repos/mitodl/micromasters/issues/comments{/number}","contents_url":"https://api.github.com/repos/mitodl/micromasters/contents/{+path}","compare_url":"https://api.github.com/repos/mitodl/micromasters/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mitodl/micromasters/merges","archive_url":"https://api.github.com/repos/mitodl/micromasters/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mitodl/micromasters/downloads","issues_url":"https://api.github.com/repos/mitodl/micromasters/issues{/number}","pulls_url":"https://api.github.com/repos/mitodl/micromasters/pulls{/number}","milestones_url":"https://api.github.com/repos/mitodl/micromasters/milestones{/number}","notifications_url":"https://api.github.com/repos/mitodl/micromasters/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mitodl/micromasters/labels{/name}","releases_url":"https://api.github.com/repos/mitodl/micromasters/releases{/id}","deployments_url":"https://api.github.com/repos/mitodl/micromasters/deployments","created_at":"2016-03-01T23:53:17Z","updated_at":"2016-12-03T13:16:59Z","pushed_at":"2016-12-05T21:10:36Z","git_url":"git://github.com/mitodl/micromasters.git","ssh_url":"git@github.com:mitodl/micromasters.git","clone_url":"https://github.com/mitodl/micromasters.git","svn_url":"https://github.com/mitodl/micromasters","homepage":"","size":29192,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":206,"forks":3,"open_issues":206,"watchers":4,"default_branch":"master"}},"base":{"label":"mitodl:master","ref":"master","sha":"49a69a06f54bbac2a2a692c6a67715f2475b4eb2","user":{"login":"mitodl","id":7918516,"avatar_url":"https://avatars.githubusercontent.com/u/7918516?v=3","gravatar_id":"","url":"https://api.github.com/users/mitodl","html_url":"https://github.com/mitodl","followers_url":"https://api.github.com/users/mitodl/followers","following_url":"https://api.github.com/users/mitodl/following{/other_user}","gists_url":"https://api.github.com/users/mitodl/gists{/gist_id}","starred_url":"https://api.github.com/users/mitodl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitodl/subscriptions","organizations_url":"https://api.github.com/users/mitodl/orgs","repos_url":"https://api.github.com/users/mitodl/repos","events_url":"https://api.github.com/users/mitodl/events{/privacy}","received_events_url":"https://api.github.com/users/mitodl/received_events","type":"Organization","site_admin":false},"repo":{"id":52919185,"name":"micromasters","full_name":"mitodl/micromasters","owner":{"login":"mitodl","id":7918516,"avatar_url":"https://avatars.githubusercontent.com/u/7918516?v=3","gravatar_id":"","url":"https://api.github.com/users/mitodl","html_url":"https://github.com/mitodl","followers_url":"https://api.github.com/users/mitodl/followers","following_url":"https://api.github.com/users/mitodl/following{/other_user}","gists_url":"https://api.github.com/users/mitodl/gists{/gist_id}","starred_url":"https://api.github.com/users/mitodl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mitodl/subscriptions","organizations_url":"https://api.github.com/users/mitodl/orgs","repos_url":"https://api.github.com/users/mitodl/repos","events_url":"https://api.github.com/users/mitodl/events{/privacy}","received_events_url":"https://api.github.com/users/mitodl/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mitodl/micromasters","description":"Portal for learners and course teams to access MITx micromasters programs: https://goo.gl/tsUTSO","fork":false,"url":"https://api.github.com/repos/mitodl/micromasters","forks_url":"https://api.github.com/repos/mitodl/micromasters/forks","keys_url":"https://api.github.com/repos/mitodl/micromasters/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mitodl/micromasters/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mitodl/micromasters/teams","hooks_url":"https://api.github.com/repos/mitodl/micromasters/hooks","issue_events_url":"https://api.github.com/repos/mitodl/micromasters/issues/events{/number}","events_url":"https://api.github.com/repos/mitodl/micromasters/events","assignees_url":"https://api.github.com/repos/mitodl/micromasters/assignees{/user}","branches_url":"https://api.github.com/repos/mitodl/micromasters/branches{/branch}","tags_url":"https://api.github.com/repos/mitodl/micromasters/tags","blobs_url":"https://api.github.com/repos/mitodl/micromasters/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mitodl/micromasters/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mitodl/micromasters/git/refs{/sha}","trees_url":"https://api.github.com/repos/mitodl/micromasters/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mitodl/micromasters/statuses/{sha}","languages_url":"https://api.github.com/repos/mitodl/micromasters/languages","stargazers_url":"https://api.github.com/repos/mitodl/micromasters/stargazers","contributors_url":"https://api.github.com/repos/mitodl/micromasters/contributors","subscribers_url":"https://api.github.com/repos/mitodl/micromasters/subscribers","subscription_url":"https://api.github.com/repos/mitodl/micromasters/subscription","commits_url":"https://api.github.com/repos/mitodl/micromasters/commits{/sha}","git_commits_url":"https://api.github.com/repos/mitodl/micromasters/git/commits{/sha}","comments_url":"https://api.github.com/repos/mitodl/micromasters/comments{/number}","issue_comment_url":"https://api.github.com/repos/mitodl/micromasters/issues/comments{/number}","contents_url":"https://api.github.com/repos/mitodl/micromasters/contents/{+path}","compare_url":"https://api.github.com/repos/mitodl/micromasters/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mitodl/micromasters/merges","archive_url":"https://api.github.com/repos/mitodl/micromasters/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mitodl/micromasters/downloads","issues_url":"https://api.github.com/repos/mitodl/micromasters/issues{/number}","pulls_url":"https://api.github.com/repos/mitodl/micromasters/pulls{/number}","milestones_url":"https://api.github.com/repos/mitodl/micromasters/milestones{/number}","notifications_url":"https://api.github.com/repos/mitodl/micromasters/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mitodl/micromasters/labels{/name}","releases_url":"https://api.github.com/repos/mitodl/micromasters/releases{/id}","deployments_url":"https://api.github.com/repos/mitodl/micromasters/deployments","created_at":"2016-03-01T23:53:17Z","updated_at":"2016-12-03T13:16:59Z","pushed_at":"2016-12-05T21:10:36Z","git_url":"git://github.com/mitodl/micromasters.git","ssh_url":"git@github.com:mitodl/micromasters.git","clone_url":"https://github.com/mitodl/micromasters.git","svn_url":"https://github.com/mitodl/micromasters","homepage":"","size":29192,"stargazers_count":4,"watchers_count":4,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":3,"mirror_url":null,"open_issues_count":206,"forks":3,"open_issues":206,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/1969"},"html":{"href":"https://github.com/mitodl/micromasters/pull/1969"},"issue":{"href":"https://api.github.com/repos/mitodl/micromasters/issues/1969"},"comments":{"href":"https://api.github.com/repos/mitodl/micromasters/issues/1969/comments"},"review_comments":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/1969/comments"},"review_comment":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/mitodl/micromasters/pulls/1969/commits"},"statuses":{"href":"https://api.github.com/repos/mitodl/micromasters/statuses/a560ce670ef5ee7961319dc89b9dfe928ee92b20"}}}}
{"actor":{"display_login":"aliceriot"}}