{"action":"created","comment":{"url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/comments/43774838","id":43774838,"diff_hunk":"@@ -0,0 +1,345 @@\n+\"\"\"\n+This module contains grading logic for Vector Drawing exercises.\n+\"\"\"\n+\n+# pylint: disable=invalid-name\n+\n+import inspect\n+import logging\n+import math\n+\n+\n+log = logging.getLogger(__name__)  # pylint: disable=invalid-name\n+\n+\n+# Built-in check functions\n+\n+def _errmsg(default_message, check, vectors):\n+    \"\"\"\n+    Return error message for `check` targeting a vector from `vectors`.\n+\n+    If `check` does not define a custom error message, fall back on `default_message`.\n+    \"\"\"\n+    template = check.get('errmsg', default_message)\n+    vec = vectors[check['vector']]\n+    return template.format(\n+        name=vec.name,\n+        tail_x=vec.tail.x,\n+        tail_y=vec.tail.y,\n+        tip_x=vec.tip.x,\n+        tip_y=vec.tip.y,\n+        length=vec.length,\n+        angle=vec.angle\n+    )\n+\n+\n+def _errmsg_point(default_message, check, point):\n+    \"\"\"\n+    Return error message for `check` targeting `point`.\n+\n+    If `check` does not define a custom error message, fall back on `default_message`.\n+    \"\"\"\n+    template = check.get('errmsg', default_message)\n+    return template.format(name=check['point'], x=point.x, y=point.y)\n+\n+\n+def check_presence(check, vectors):\n+    \"\"\"\n+    Check if `vectors` contains vector targeted by `check`.\n+    \"\"\"\n+    if check['vector'] not in vectors:\n+        errmsg = check.get('errmsg', 'You need to use the {name} vector.')\n+        return errmsg.format(name=check['vector'])\n+\n+\n+def check_tail(check, vectors):\n+    \"\"\"\n+    Check if tail of vector targeted by `check` is in correct position.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 1.0)\n+    expected = check['expected']\n+    dist = math.hypot(expected[0] - vec.tail.x, expected[1] - vec.tail.y)\n+    if dist > tolerance:\n+        return _errmsg('Vector {name} does not start at correct point.', check, vectors)\n+\n+\n+def check_tip(check, vectors):\n+    \"\"\"\n+    Check if tip of vector targeted by `check` is in correct position.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 1.0)\n+    expected = check['expected']\n+    dist = math.hypot(expected[0] - vec.tip.x, expected[1] - vec.tip.y)\n+    if dist > tolerance:\n+        return _errmsg('Vector {name} does not end at correct point.', check, vectors)\n+\n+\n+def _check_coordinate(check, coord):\n+    \"\"\"\n+    Check `coord` against expected value.\n+    \"\"\"\n+    tolerance = check.get('tolerance', 1.0)\n+    expected = check['expected']\n+    return abs(expected - coord) > tolerance\n+\n+\n+def check_tail_x(check, vectors):\n+    \"\"\"\n+    Check if x position of tail of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    if _check_coordinate(check, vec.tail.x):\n+        return _errmsg('Vector {name} does not start at correct point.', check, vectors)\n+\n+\n+def check_tail_y(check, vectors):\n+    \"\"\"\n+    Check if y position of tail of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    if _check_coordinate(check, vec.tail.y):\n+        return _errmsg('Vector {name} does not start at correct point.', check, vectors)\n+\n+\n+def check_tip_x(check, vectors):\n+    \"\"\"\n+    Check if x position of tip of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    if _check_coordinate(check, vec.tip.x):\n+        return _errmsg('Vector {name} does not end at correct point.', check, vectors)\n+\n+\n+def check_tip_y(check, vectors):\n+    \"\"\"\n+    Check if y position of tip of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    if _check_coordinate(check, vec.tip.y):\n+        return _errmsg('Vector {name} does not end at correct point.', check, vectors)\n+\n+\n+def _coord_delta(expected, actual):\n+    \"\"\"\n+    Return distance between `expected` and `actual` coordinates.\n+    \"\"\"\n+    if expected == '_':\n+        return 0\n+    else:\n+        return expected - actual\n+\n+\n+def _coords_within_tolerance(vec, expected, tolerance):\n+    \"\"\"\n+    Check if distance between coordinates of `vec` and `expected` coordinates is within `tolerance`.\n+    \"\"\"\n+    for expected_coords, vec_coords in ([expected[0], vec.tail], [expected[1], vec.tip]):\n+        delta_x = _coord_delta(expected_coords[0], vec_coords.x)\n+        delta_y = _coord_delta(expected_coords[1], vec_coords.y)\n+        if math.hypot(delta_x, delta_y) > tolerance:\n+            return False\n+    return True\n+\n+\n+def check_coords(check, vectors):\n+    \"\"\"\n+    Check if coordinates of vector targeted by `check` are in correct position.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    expected = check['expected']\n+    tolerance = check.get('tolerance', 1.0)\n+    if not _coords_within_tolerance(vec, expected, tolerance):\n+        return _errmsg('Vector {name} coordinates are not correct.', check, vectors)\n+\n+\n+def check_segment_coords(check, vectors):\n+    \"\"\"\n+    Check if coordinates of segment targeted by `check` are in correct position.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    expected = check['expected']\n+    tolerance = check.get('tolerance', 1.0)\n+    if not (_coords_within_tolerance(vec, expected, tolerance) or\n+            _coords_within_tolerance(vec.opposite(), expected, tolerance)):\n+        return _errmsg('Segment {name} coordinates are not correct.', check, vectors)\n+\n+\n+def check_length(check, vectors):\n+    \"\"\"\n+    Check if length of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 1.0)\n+    if abs(vec.length - check['expected']) > tolerance:\n+        return _errmsg(\n+            'The length of {name} is incorrect. Your length: {length:.1f}', check, vectors\n+        )\n+\n+\n+def _angle_within_tolerance(vec, expected, tolerance):\n+    \"\"\"\n+    Check if difference between angle of `vec` and `expected` angle is within `tolerance`.\n+    \"\"\"\n+    # Calculate angle between vec and identity vector with expected angle\n+    # using the formula:\n+    # angle = acos((A . B) / len(A)*len(B))\n+    x = vec.tip.x - vec.tail.x\n+    y = vec.tip.y - vec.tail.y\n+    dot_product = x * math.cos(expected) + y * math.sin(expected)\n+    angle = math.degrees(math.acos(dot_product / vec.length))\n+    return abs(angle) <= tolerance\n+\n+\n+def check_angle(check, vectors):\n+    \"\"\"\n+    Check if angle of vector targeted by `check` is correct.\n+    \"\"\"\n+    vec = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 2.0)\n+    expected = math.radians(check['expected'])\n+    if not _angle_within_tolerance(vec, expected, tolerance):\n+        return _errmsg('The angle of {name} is incorrect. Your angle: {angle:.1f}', check, vectors)\n+\n+\n+def check_segment_angle(check, vectors):\n+    \"\"\"\n+    Check if angle of segment targeted by `check` is correct.\n+    \"\"\"\n+    # Segments are not directed, so we must check the angle between the segment and\n+    # the vector that represents it, as well as its opposite vector.\n+    vec = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 2.0)\n+    expected = math.radians(check['expected'])\n+    if not (_angle_within_tolerance(vec, expected, tolerance) or\n+            _angle_within_tolerance(vec.opposite(), expected, tolerance)):\n+        return _errmsg('The angle of {name} is incorrect. Your angle: {angle:.1f}', check, vectors)\n+\n+\n+def _dist_line_point(line, point):\n+    \"\"\"\n+    Return distance between `line` and `point`.\n+\n+    The line is passed in as a Vector instance, the point as a Point instance.\n+    \"\"\"\n+    direction_x = line.tip.x - line.tail.x\n+    direction_y = line.tip.y - line.tail.y\n+    determinant = (point.x - line.tail.x) * direction_y - (point.y - line.tail.y) * direction_x\n+    return abs(determinant) / math.hypot(direction_x, direction_y)\n+\n+\n+def check_points_on_line(check, vectors):\n+    \"\"\"\n+    Check if line targeted by `check` passes through correct points.\n+    \"\"\"\n+    line = vectors[check['vector']]\n+    tolerance = check.get('tolerance', 1.0)\n+    points = check.get('expected')\n+    for point in points:\n+        point = Point(point[0], point[1])\n+        if _dist_line_point(line, point) > tolerance:\n+            return _errmsg(\n+                'The line {name} does not pass through the correct points.', check, vectors\n+            )\n+\n+\n+def check_point_coords(check, points):\n+    \"\"\"\n+    Check if coordinates of point targeted by `check` are correct.\n+    \"\"\"\n+    point = points[check['point']]\n+    tolerance = check.get('tolerance', 1.0)\n+    expected = check.get('expected')\n+    dist = math.hypot(expected[0] - point.x, expected[1] - point.y)\n+    if dist > tolerance:\n+        return _errmsg_point('Point {name} is not at the correct location.', check, point)\n+\n+\n+class Point(object):\n+    \"\"\" Represents a single point on the vector drawing board. \"\"\"\n+    def __init__(self, x, y):\n+        self.x = x\n+        self.y = y\n+\n+\n+class Vector(object):\n+    \"\"\" Represents a single vector on the vector drawing board. \"\"\"\n+    def __init__(self, name, x1, y1, x2, y2):  # pylint: disable=too-many-arguments\n+        self.name = name\n+        self.tail = Point(x1, y1)\n+        self.tip = Point(x2, y2)\n+        self.length = math.hypot(x2 - x1, y2 - y1)\n+        angle = math.degrees(math.atan2(y2 - y1, x2 - x1))\n+        if angle < 0:\n+            angle += 360\n+        self.angle = angle\n+\n+    def opposite(self):\n+        \"\"\"\n+        Return new vector with tip and tail swapped.\n+        \"\"\"\n+        return Vector(self.name, self.tip.x, self.tip.y, self.tail.x, self.tail.y)\n+\n+\n+class Grader(object):\n+    \"\"\"\n+    Implements grading logic for student answers to Vector Drawing exercises.\n+    \"\"\"\n+    check_registry = {\n+        'presence': check_presence,\n+        'tail': check_tail,\n+        'tip': check_tip,\n+        'tail_x': check_tail_x,\n+        'tail_y': check_tail_y,\n+        'tip_x': check_tip_x,\n+        'tip_y': check_tip_y,\n+        'coords': check_coords,\n+        'length': check_length,\n+        'angle': check_angle,\n+        'segment_angle': check_segment_angle,\n+        'segment_coords': check_segment_coords,\n+        'points_on_line': check_points_on_line,\n+        'point_coords': check_point_coords,\n+    }\n+\n+    def __init__(self, success_message='Test passed', custom_checks=None):\n+        self.success_message = success_message\n+        if custom_checks:\n+            self.check_registry.update(custom_checks)\n+\n+    def grade(self, answer):\n+        \"\"\"\n+        Check correctness of `answer` by running checks defined for it one by one.\n+\n+        Short-circuit as soon as a single check fails.\n+        \"\"\"\n+        check_data = dict(\n+            vectors=self._get_vectors(answer),\n+            points=self._get_points(answer),\n+        )\n+        for check in answer['checks']:\n+            check_data['check'] = check\n+            check_fn = self.check_registry[check['check']]\n+            args = [check_data[arg] for arg in inspect.getargspec(check_fn).args]","path":"vectordraw/grader.py","position":324,"original_position":324,"commit_id":"fbc4ea4885de2deb0a006bc7e9f5d59595270349","original_commit_id":"fbc4ea4885de2deb0a006bc7e9f5d59595270349","user":{"login":"smarnach","id":249196,"avatar_url":"https://avatars.githubusercontent.com/u/249196?v=3","gravatar_id":"","url":"https://api.github.com/users/smarnach","html_url":"https://github.com/smarnach","followers_url":"https://api.github.com/users/smarnach/followers","following_url":"https://api.github.com/users/smarnach/following{/other_user}","gists_url":"https://api.github.com/users/smarnach/gists{/gist_id}","starred_url":"https://api.github.com/users/smarnach/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/smarnach/subscriptions","organizations_url":"https://api.github.com/users/smarnach/orgs","repos_url":"https://api.github.com/users/smarnach/repos","events_url":"https://api.github.com/users/smarnach/events{/privacy}","received_events_url":"https://api.github.com/users/smarnach/received_events","type":"User","site_admin":false},"body":"I just noticed this hack I introduced when adding points as a problem type.  It's main purpose was to stay backwards-compatible with existing external graders.  We might now be able to simply pass `check, vectors, points` as arguments to all graders.  (Up to you whether you want to change this.  it would require editing the parameter lists of all functions in this file, and updaing all the tests.)","created_at":"2015-11-03T16:59:46Z","updated_at":"2015-11-03T16:59:46Z","html_url":"https://github.com/open-craft/xblock-vectordraw/pull/1#discussion_r43774838","pull_request_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/comments/43774838"},"html":{"href":"https://github.com/open-craft/xblock-vectordraw/pull/1#discussion_r43774838"},"pull_request":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1","id":49297422,"html_url":"https://github.com/open-craft/xblock-vectordraw/pull/1","diff_url":"https://github.com/open-craft/xblock-vectordraw/pull/1.diff","patch_url":"https://github.com/open-craft/xblock-vectordraw/pull/1.patch","issue_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/1","number":1,"state":"open","locked":false,"title":"Initial implementation","user":{"login":"itsjeyd","id":961441,"avatar_url":"https://avatars.githubusercontent.com/u/961441?v=3","gravatar_id":"","url":"https://api.github.com/users/itsjeyd","html_url":"https://github.com/itsjeyd","followers_url":"https://api.github.com/users/itsjeyd/followers","following_url":"https://api.github.com/users/itsjeyd/following{/other_user}","gists_url":"https://api.github.com/users/itsjeyd/gists{/gist_id}","starred_url":"https://api.github.com/users/itsjeyd/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/itsjeyd/subscriptions","organizations_url":"https://api.github.com/users/itsjeyd/orgs","repos_url":"https://api.github.com/users/itsjeyd/repos","events_url":"https://api.github.com/users/itsjeyd/events{/privacy}","received_events_url":"https://api.github.com/users/itsjeyd/received_events","type":"User","site_admin":false},"body":"**Testing**\r\n\r\n1. Install the XBlock on Devstack via:\r\n\r\n        pip install -e .\r\n\r\n2. Add `vectordraw` to the Advanced Module List in your course settings.\r\n3. Add a \"Vector Drawing\" block to a unit.\r\n4. Customize the Vector Drawing block. You can use one of the examples from the jsinput-vectordraw repository (e.g., [2_boxIncline_multiVector](https://raw.githubusercontent.com/open-craft/jsinput-vectordraw/master/Notes_and_Examples/2_boxIncline_multiVector/2_boxIncline.html)) for this purpose. Note that values of \"Vectors\", \"Points\", \"Expected result\" settings need to be valid JSON.\r\n","created_at":"2015-10-30T17:18:57Z","updated_at":"2015-11-03T16:59:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"3da4bd6e01e3b95034eaff5e2cc9dafee39223f6","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1/commits","review_comments_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1/comments","review_comment_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/comments{/number}","comments_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/1/comments","statuses_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/statuses/fbc4ea4885de2deb0a006bc7e9f5d59595270349","head":{"label":"open-craft:initial-implementation","ref":"initial-implementation","sha":"fbc4ea4885de2deb0a006bc7e9f5d59595270349","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":45102882,"name":"xblock-vectordraw","full_name":"open-craft/xblock-vectordraw","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/xblock-vectordraw","description":"An XBlock for creating vector drawing exercises","fork":false,"url":"https://api.github.com/repos/open-craft/xblock-vectordraw","forks_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/forks","keys_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/teams","hooks_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/hooks","issue_events_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/events","assignees_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/tags","blobs_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/languages","stargazers_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/stargazers","contributors_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/contributors","subscribers_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/subscribers","subscription_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/subscription","commits_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/merges","archive_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/downloads","issues_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/releases{/id}","created_at":"2015-10-28T09:45:59Z","updated_at":"2015-10-28T09:45:59Z","pushed_at":"2015-11-03T15:57:48Z","git_url":"git://github.com/open-craft/xblock-vectordraw.git","ssh_url":"git@github.com:open-craft/xblock-vectordraw.git","clone_url":"https://github.com/open-craft/xblock-vectordraw.git","svn_url":"https://github.com/open-craft/xblock-vectordraw","homepage":null,"size":0,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"base":{"label":"open-craft:master","ref":"master","sha":"4fdb02a55a5e5ccc9824e2a85147dc9589e29d53","user":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"repo":{"id":45102882,"name":"xblock-vectordraw","full_name":"open-craft/xblock-vectordraw","owner":{"login":"open-craft","id":7414786,"avatar_url":"https://avatars.githubusercontent.com/u/7414786?v=3","gravatar_id":"","url":"https://api.github.com/users/open-craft","html_url":"https://github.com/open-craft","followers_url":"https://api.github.com/users/open-craft/followers","following_url":"https://api.github.com/users/open-craft/following{/other_user}","gists_url":"https://api.github.com/users/open-craft/gists{/gist_id}","starred_url":"https://api.github.com/users/open-craft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/open-craft/subscriptions","organizations_url":"https://api.github.com/users/open-craft/orgs","repos_url":"https://api.github.com/users/open-craft/repos","events_url":"https://api.github.com/users/open-craft/events{/privacy}","received_events_url":"https://api.github.com/users/open-craft/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/open-craft/xblock-vectordraw","description":"An XBlock for creating vector drawing exercises","fork":false,"url":"https://api.github.com/repos/open-craft/xblock-vectordraw","forks_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/forks","keys_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/keys{/key_id}","collaborators_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/teams","hooks_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/hooks","issue_events_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/events{/number}","events_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/events","assignees_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/assignees{/user}","branches_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/branches{/branch}","tags_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/tags","blobs_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/refs{/sha}","trees_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/trees{/sha}","statuses_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/statuses/{sha}","languages_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/languages","stargazers_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/stargazers","contributors_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/contributors","subscribers_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/subscribers","subscription_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/subscription","commits_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/commits{/sha}","git_commits_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/git/commits{/sha}","comments_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/comments{/number}","issue_comment_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/comments{/number}","contents_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/contents/{+path}","compare_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/compare/{base}...{head}","merges_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/merges","archive_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/downloads","issues_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues{/number}","pulls_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls{/number}","milestones_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/milestones{/number}","notifications_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/labels{/name}","releases_url":"https://api.github.com/repos/open-craft/xblock-vectordraw/releases{/id}","created_at":"2015-10-28T09:45:59Z","updated_at":"2015-10-28T09:45:59Z","pushed_at":"2015-11-03T15:57:48Z","git_url":"git://github.com/open-craft/xblock-vectordraw.git","ssh_url":"git@github.com:open-craft/xblock-vectordraw.git","clone_url":"https://github.com/open-craft/xblock-vectordraw.git","svn_url":"https://github.com/open-craft/xblock-vectordraw","homepage":null,"size":0,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1"},"html":{"href":"https://github.com/open-craft/xblock-vectordraw/pull/1"},"issue":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/1"},"comments":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/open-craft/xblock-vectordraw/statuses/fbc4ea4885de2deb0a006bc7e9f5d59595270349"}}}}