{"action":"created","comment":{"url":"https://api.github.com/repos/odlgroup/odl/pulls/comments/88010858","pull_request_review_id":8597064,"id":88010858,"diff_hunk":"@@ -0,0 +1,472 @@\n+# Copyright 2014-2016 The ODL development group\n+#\n+# This file is part of ODL.\n+#\n+# ODL is free software: you can redistribute it and/or modify\n+# it under the terms of the GNU General Public License as published by\n+# the Free Software Foundation, either version 3 of the License, or\n+# (at your option) any later version.\n+#\n+# ODL is distributed in the hope that it will be useful,\n+# but WITHOUT ANY WARRANTY; without even the implied warranty of\n+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+# GNU General Public License for more details.\n+#\n+# You should have received a copy of the GNU General Public License\n+# along with ODL.  If not, see <http://www.gnu.org/licenses/>.\n+\n+\"\"\"Utilities and class for reading uncompressed binary files with header.\"\"\"\n+\n+import csv\n+import numpy as np\n+import struct\n+\n+\n+__all__ = ('FileReaderUncompressedBinary',)\n+\n+\n+# TODO: add rest\n+DTYPE_MAP_NPY2STRUCT = {\n+    np.dtype('float32'): 'f',\n+    np.dtype('int32'): 'i',\n+    np.dtype('int16'): 'h',\n+    np.dtype('int8'): 'b',\n+    np.dtype('uint64'): 'L',\n+    np.dtype('uint32'): 'I',\n+    np.dtype('uint16'): 'H',\n+    np.dtype('uint8'): 'B',\n+    np.dtype('S1'): 'b'}\n+\n+\n+def reformat_spec(spec):\n+    \"\"\"Return the reformatted specification table.\n+\n+    The given specification is assumed to be in\n+    `reST grid table format\n+    <http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables>`_.\n+\n+    Parameters\n+    ----------\n+    spec : str\n+        Specification given as a string containing a definition table.\n+\n+    Returns\n+    -------\n+    lines : tuple of strings\n+        Table lines with leading and trailing '|' stripped and lines\n+        containing '...' removed.\n+    \"\"\"\n+    return tuple(line[1:-1].rstrip() for line in spec.splitlines()\n+                 if line.startswith('|') and '...' not in line)\n+\n+\n+def spec_fields(spec, id_key):\n+    \"\"\"Read the specification and return a list of fields.\n+\n+    The given specification is assumed to be in\n+    `reST grid table format\n+    <http://docutils.sourceforge.net/docs/user/rst/quickref.html#tables>`_.\n+\n+    Parameters\n+    ----------\n+    spec : str\n+        Specification given as a string containing a definition table.\n+    id_key : str\n+        Dictionary key (= column header) for the ID labels in ``spec``.\n+\n+    Returns\n+    -------\n+    fields : tuple of dicts\n+        Field list of the specification with combined multi-line entries.\n+        Each field corresponds to one (multi-)line of the spec.\n+    \"\"\"\n+    spec_lines = reformat_spec(spec)\n+\n+    # Guess the CSV dialect and read the table, producing an iterable\n+    dialect = csv.Sniffer().sniff(spec_lines[0], delimiters='|')\n+    reader = csv.DictReader(spec_lines, dialect=dialect)\n+\n+    # Read the fields as dictionaries and transform keys and values to\n+    # lowercase.\n+    fields = []\n+    for row in reader:\n+        new_row = {}\n+        if row[id_key].strip():\n+            # Start of a new field, indicated by a nontrivial ID entry\n+            for key, val in row.items():\n+                new_row[key.strip()] = val.strip()\n+            fields.append(new_row)\n+        else:\n+            # We have the second row of a multi-line field. We\n+            # append all stripped values to the corresponding existing entry\n+            # value with an extra space.\n+\n+            if not fields:\n+                # Just to make sure that this situation did not happen at\n+                # the very beginning of the table\n+                continue\n+\n+            for key, val in row.items():\n+                fields[-1][key.strip()] += (' ' + val).rstrip()\n+\n+    return tuple(fields)\n+\n+\n+def standardized_fields(field_list, keys, dtype_map):\n+    \"\"\"Convert the field keys and values to standard format.\n+\n+    Data type is piped through `numpy.dtype`, and name is converted\n+    to lowercase. All keys are converted to lowercase.\n+\n+    The standardized fields are as follows:\n+\n+    +--------------+---------+------------------------------------------+\n+    |Name          |Data type|Description                               |\n+    +==============+=========+==========================================+\n+    |'name'        |string   |Name of the element                       |\n+    +--------------+---------+------------------------------------------+\n+    |'offset_bytes'|int      |Offset of the current element in bytes    |\n+    +--------------+---------+------------------------------------------+\n+    |'size_bytes'  |int      |Size of the current element in bytes      |\n+    +--------------+---------+------------------------------------------+\n+    |'dtype'       |type     |Data type of the current element as       |\n+    |              |         |defined by Numpy                          |\n+    +--------------+---------+------------------------------------------+\n+    |'description' |string   |Description of the element (optional)     |\n+    +--------------+---------+------------------------------------------+\n+    |'dshape'      |tuple    |For multi-elements: number of elements per|\n+    |              |         |dimension. Optional for single elements.  |\n+    +--------------+---------+------------------------------------------+\n+\n+    Parameters\n+    ----------\n+    field_list : sequence of dicts\n+        Dictionaries describing the field elements, as returned by, e.g.,\n+        `mrc_spec_fields`.\n+    keys : dict\n+        Dictionary with the following entries for the column headers in\n+        the specification table:\n+\n+            - ``'id'``\n+            - ``'byte_range'``\n+            - ``'dtype'``\n+            - ``'name'``\n+            - ``'description'``\n+\n+    dtype_map : dict\n+        Mapping from the data type specifiers in the specification table\n+        to NumPy data types.\n+\n+    Returns\n+    -------\n+    standardized_fields : tuple of dicts\n+        The standardized fields according to the above table.\n+    \"\"\"\n+    # Parse the fields and represent them in a unfied way\n+    conv_list = []\n+    for row, field in enumerate(field_list):\n+        new_field = {}\n+\n+        # Name and description: lowercase name, copy description\n+        new_field['name'] = field[keys['name']].lower()\n+        new_field['description'] = field[keys['description']]\n+\n+        # Get offset from ID range\n+        num_range = field[keys['id']].split('-')\n+        nstart = int(num_range[0])\n+        nend = int(num_range[-1])  # 0 for range of type 3-\n+\n+        # Get byte range and set start\n+        byte_range = field[keys['byte_range']].split('-')\n+        byte_start = int(byte_range[0]) - 1\n+        byte_end = int(byte_range[-1]) - 1\n+        new_field['offset_bytes'] = byte_start\n+\n+        # Data type: transform to Numpy format and get shape from its\n+        # itemsize and the byte range\n+        dtype = dtype_map[field[keys['dtype']]]\n+        byte_size = byte_end - byte_start + 1\n+\n+        if hasattr(dtype, 'itemsize') and byte_size % dtype.itemsize:\n+            raise ValueError(\n+                'in row {}: byte range {} not a multiple of itemsize {} '\n+                'of the data type {}.'\n+                ''.format(row + 1, field[keys['byte_range']], dtype.itemsize,\n+                          field[keys['dtype']]))\n+\n+        new_field['dtype'] = dtype\n+        new_field['size_bytes'] = byte_size\n+        # Assuming 1d arrangement of multiple elements\n+        # TODO: find way to handle 2d fields\n+        if nend:\n+            new_field['dshape'] = (nend - nstart + 1,)\n+        elif hasattr(dtype, 'itemsize'):\n+            new_field['dshape'] = (byte_size / dtype.itemsize,)\n+        else:\n+            new_field['dshape'] = (1,)\n+\n+        conv_list.append(new_field)\n+\n+    return tuple(conv_list)\n+\n+\n+class FileReaderUncompressedBinary(object):\n+\n+    \"\"\"Reader for uncompressed binary files including a header.\"\"\"\n+\n+    def __init__(self, file, header_fields):\n+        \"\"\"Initialize a new instance.\n+\n+        Parameters\n+        ----------\n+        file : file-like or str\n+            Stream or filename from which to read the data. The stream\n+            is allowed to be already opened in 'rb' mode.\n+        header_fields : sequence of dicts\n+            Definition of the fields in the header (per row), each\n+            containing key-value pairs for the following keys:\n+\n+            - ``'name'`` : Label for the field.\n+            - ``'offset_bytes'`` : Start of the field in bytes.\n+            - ``'size_bytes'`` : Size of the field in bytes.\n+            - ``'dtype'`` : Data type in Numpy- or Numpy-readable format.\n+            - ``'dshape'`` (optional) : The array of values is reshaped to\n+              this shape.\n+            - ``'description'`` : A human-readable description of the field.\n+        \"\"\"\n+        try:\n+            f = open(file, 'rb')\n+        except TypeError:\n+            f = file\n+\n+        if f.mode != 'rb':\n+            raise ValueError(\"`file` must be opened in 'rb' mode, but mode \"\n+                             \"'is {}'\".format(f.mode))\n+\n+        self.file = f\n+\n+        # Initialize some attributes to default values, plus the header\n+        self.data_shape = -1  # Makes reshape a no-op\n+        self.header_bytes = None\n+        self.header_fields = header_fields\n+        self.data = None\n+        if self.header_fields:\n+            self.header = self.read_header()\n+        else:\n+            self.header = None\n+\n+    @classmethod\n+    def from_raw_file(cls, file, header_bytes, dtype):\n+        \"\"\"Construct a reader from a raw file w/o header spec.\n+\n+        Readers constructed with this method can use `read_data`, but\n+        not `read_header`.\n+\n+        Parameters\n+        ----------\n+        file : file-like or str\n+            Stream or filename from which to read the data. The stream\n+            is allowed to be already opened in 'rb' mode.\n+        header_bytes : int\n+            Size of the header in bytes.\n+        dtype :\n+            Data type specifier for the data field. It must be\n+            understood by the `numpy.dtype` constructor.\n+\n+        Returns\n+        -------\n+        reader : `MRCFileReader`\n+            Raw reader for the given MRC file.\n+        \"\"\"\n+        try:\n+            f = open(file, 'rb')\n+        except TypeError:\n+            f = file\n+\n+        if f.mode != 'rb':\n+            raise ValueError(\"`file` must be opened in 'rb' mode, got '{}'\"\n+                             \"\".format(f.mode))\n+\n+        header_bytes = int(header_bytes)\n+        if header_bytes < 0:\n+            raise ValueError('`header_bytes` must be nonnegative, got {}.'\n+                             ''.format(header_bytes))\n+\n+        filesize_bytes = f.seek(0, 2)\n+        data_bytes = filesize_bytes - header_bytes\n+        f.seek(0)\n+\n+        if header_bytes >= filesize_bytes:\n+            raise ValueError('`header_bytes` is larger or equal to file size '\n+                             '({} >= {})'.format(header_bytes, filesize_bytes))\n+\n+        if dtype is None:\n+            raise TypeError('`dtype` cannot be `None`')\n+        dtype = np.dtype(dtype)\n+        data_size = data_bytes / dtype.itemsize\n+\n+        instance = cls(f, header_fields=[])\n+        instance.data_dtype = dtype\n+        instance.data_shape = (data_size,)\n+        instance.header_bytes = header_bytes\n+        return instance\n+\n+    def read_header(self):\n+        \"\"\"Read the header from the reader's file.\n+\n+        The header is also stored in the ``self.header`` attribute.\n+\n+        Returns\n+        -------\n+        header : dict\n+            Header of ``self.file`` stored in a dictionary, where each\n+            entry has the following form::\n+\n+                'name': {'value': value, 'description': description}\n+\n+            All ``value``'s are `numpy.ndarray`'s with at least one\n+            dimension. If a ``shape`` is given in ``self.header_fields``,\n+            the resulting array is reshaped accordingly.\n+        \"\"\"\n+        # Read all fields except data\n+        header = {}\n+        for field in self.header_fields:\n+            # Get all the values from the dictionary\n+            name = field['name']\n+            if name == 'data':\n+                continue\n+            entry = {'description': field.get('description', '')}\n+            offset_bytes = field['offset_bytes']\n+            size_bytes = field.get('size_bytes', None)\n+            dtype = field['dtype']\n+            shape = field.get('dshape', None)\n+            if shape is None:\n+                shape = -1  # Causes reshape to 1d or no-op\n+\n+            bytes_per_elem = dtype.itemsize\n+\n+            if size_bytes is None:\n+                # Default if 'size_bytes' is omitted\n+                num_elems = 1\n+            else:\n+                num_elems = size_bytes / bytes_per_elem\n+\n+            if num_elems != int(num_elems):\n+                raise RuntimeError(\n+                    \"field '{}': `size_bytes` {} and `dtype.itemsize` {} \"\n+                    \" result in non-integer number of elements\"\n+                    \"\".format(name, size_bytes, bytes_per_elem))\n+\n+            # Create format string for struct module to unpack the binary\n+            # data\n+            fmt = str(int(num_elems)) + DTYPE_MAP_NPY2STRUCT[dtype]\n+            if struct.calcsize(fmt) != size_bytes:\n+                raise RuntimeError(\n+                    \"field '{}': format '{}' has results in {} bytes, but \"\n+                    \"`size_bytes` is {}\"\n+                    \"\".format(name, fmt, struct.calcsize(fmt), size_bytes))\n+\n+            self.file.seek(offset_bytes)\n+            packed_value = self.file.read(size_bytes)\n+            value = np.array(struct.unpack_from(fmt, packed_value),\n+                             dtype=dtype)\n+\n+            if dtype == np.dtype('S1'):","path":"odl/tomo/data/uncompr_bin.py","position":374,"original_position":374,"commit_id":"2aa5b3136488bb56f1db9eeb71c06b3032c6869f","original_commit_id":"2aa5b3136488bb56f1db9eeb71c06b3032c6869f","user":{"login":"kohr-h","id":5030250,"avatar_url":"https://avatars.githubusercontent.com/u/5030250?v=3","gravatar_id":"","url":"https://api.github.com/users/kohr-h","html_url":"https://github.com/kohr-h","followers_url":"https://api.github.com/users/kohr-h/followers","following_url":"https://api.github.com/users/kohr-h/following{/other_user}","gists_url":"https://api.github.com/users/kohr-h/gists{/gist_id}","starred_url":"https://api.github.com/users/kohr-h/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kohr-h/subscriptions","organizations_url":"https://api.github.com/users/kohr-h/orgs","repos_url":"https://api.github.com/users/kohr-h/repos","events_url":"https://api.github.com/users/kohr-h/events{/privacy}","received_events_url":"https://api.github.com/users/kohr-h/received_events","type":"User","site_admin":false},"body":"For me this is the simplest way of making the string case consistent with the other cases. The specification says \"4 bytes of type Char\", which is equivalent to `np.array(..., dtype='S1').shape == (4,)`. I may need to not `struct.unpack` them if they are to be read as strings. Some testing required.","created_at":"2016-11-15T13:02:08Z","updated_at":"2016-11-15T13:02:08Z","html_url":"https://github.com/odlgroup/odl/pull/716#discussion_r88010858","pull_request_url":"https://api.github.com/repos/odlgroup/odl/pulls/716","_links":{"self":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/comments/88010858"},"html":{"href":"https://github.com/odlgroup/odl/pull/716#discussion_r88010858"},"pull_request":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/716"}}},"pull_request":{"url":"https://api.github.com/repos/odlgroup/odl/pulls/716","id":93618225,"html_url":"https://github.com/odlgroup/odl/pull/716","diff_url":"https://github.com/odlgroup/odl/pull/716.diff","patch_url":"https://github.com/odlgroup/odl/pull/716.patch","issue_url":"https://api.github.com/repos/odlgroup/odl/issues/716","number":716,"state":"open","locked":false,"title":"ENH: add reader for MRC2014 EM data","user":{"login":"kohr-h","id":5030250,"avatar_url":"https://avatars.githubusercontent.com/u/5030250?v=3","gravatar_id":"","url":"https://api.github.com/users/kohr-h","html_url":"https://github.com/kohr-h","followers_url":"https://api.github.com/users/kohr-h/followers","following_url":"https://api.github.com/users/kohr-h/following{/other_user}","gists_url":"https://api.github.com/users/kohr-h/gists{/gist_id}","starred_url":"https://api.github.com/users/kohr-h/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kohr-h/subscriptions","organizations_url":"https://api.github.com/users/kohr-h/orgs","repos_url":"https://api.github.com/users/kohr-h/repos","events_url":"https://api.github.com/users/kohr-h/events{/privacy}","received_events_url":"https://api.github.com/users/kohr-h/received_events","type":"User","site_admin":false},"body":"Initial pull - a writer is on the TODO list. What's the best way of doing it? A separate writer class? Or a general `BlaFile` class? Having to open a new file in different mode from the original one would speak against it. Thoughts?\r\n\r\nTODOs:\r\n- [x] fix tests (PEP8 probably)\r\n- [ ] fix the stuff below\r\n- [ ] add writer\r\n- [ ] add example\r\n- [ ] add test combining read/write","created_at":"2016-11-14T18:12:45Z","updated_at":"2016-11-15T13:02:08Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f1180b3797d8eac94c124c885cfee161717a6bc0","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/odlgroup/odl/pulls/716/commits","review_comments_url":"https://api.github.com/repos/odlgroup/odl/pulls/716/comments","review_comment_url":"https://api.github.com/repos/odlgroup/odl/pulls/comments{/number}","comments_url":"https://api.github.com/repos/odlgroup/odl/issues/716/comments","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/2aa5b3136488bb56f1db9eeb71c06b3032c6869f","head":{"label":"odlgroup:mrc_data","ref":"mrc_data","sha":"2aa5b3136488bb56f1db9eeb71c06b3032c6869f","user":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":45596393,"name":"odl","full_name":"odlgroup/odl","owner":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odlgroup/odl","description":"Operator Discretization Library","fork":false,"url":"https://api.github.com/repos/odlgroup/odl","forks_url":"https://api.github.com/repos/odlgroup/odl/forks","keys_url":"https://api.github.com/repos/odlgroup/odl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odlgroup/odl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odlgroup/odl/teams","hooks_url":"https://api.github.com/repos/odlgroup/odl/hooks","issue_events_url":"https://api.github.com/repos/odlgroup/odl/issues/events{/number}","events_url":"https://api.github.com/repos/odlgroup/odl/events","assignees_url":"https://api.github.com/repos/odlgroup/odl/assignees{/user}","branches_url":"https://api.github.com/repos/odlgroup/odl/branches{/branch}","tags_url":"https://api.github.com/repos/odlgroup/odl/tags","blobs_url":"https://api.github.com/repos/odlgroup/odl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odlgroup/odl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odlgroup/odl/git/refs{/sha}","trees_url":"https://api.github.com/repos/odlgroup/odl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/{sha}","languages_url":"https://api.github.com/repos/odlgroup/odl/languages","stargazers_url":"https://api.github.com/repos/odlgroup/odl/stargazers","contributors_url":"https://api.github.com/repos/odlgroup/odl/contributors","subscribers_url":"https://api.github.com/repos/odlgroup/odl/subscribers","subscription_url":"https://api.github.com/repos/odlgroup/odl/subscription","commits_url":"https://api.github.com/repos/odlgroup/odl/commits{/sha}","git_commits_url":"https://api.github.com/repos/odlgroup/odl/git/commits{/sha}","comments_url":"https://api.github.com/repos/odlgroup/odl/comments{/number}","issue_comment_url":"https://api.github.com/repos/odlgroup/odl/issues/comments{/number}","contents_url":"https://api.github.com/repos/odlgroup/odl/contents/{+path}","compare_url":"https://api.github.com/repos/odlgroup/odl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odlgroup/odl/merges","archive_url":"https://api.github.com/repos/odlgroup/odl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odlgroup/odl/downloads","issues_url":"https://api.github.com/repos/odlgroup/odl/issues{/number}","pulls_url":"https://api.github.com/repos/odlgroup/odl/pulls{/number}","milestones_url":"https://api.github.com/repos/odlgroup/odl/milestones{/number}","notifications_url":"https://api.github.com/repos/odlgroup/odl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odlgroup/odl/labels{/name}","releases_url":"https://api.github.com/repos/odlgroup/odl/releases{/id}","deployments_url":"https://api.github.com/repos/odlgroup/odl/deployments","created_at":"2015-11-05T07:57:55Z","updated_at":"2016-11-03T16:59:19Z","pushed_at":"2016-11-15T10:22:45Z","git_url":"git://github.com/odlgroup/odl.git","ssh_url":"git@github.com:odlgroup/odl.git","clone_url":"https://github.com/odlgroup/odl.git","svn_url":"https://github.com/odlgroup/odl","homepage":null,"size":33444,"stargazers_count":15,"watchers_count":15,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":7,"mirror_url":null,"open_issues_count":142,"forks":7,"open_issues":142,"watchers":15,"default_branch":"master"}},"base":{"label":"odlgroup:master","ref":"master","sha":"500515186d823524d7c999525a14f64e49f27dde","user":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"repo":{"id":45596393,"name":"odl","full_name":"odlgroup/odl","owner":{"login":"odlgroup","id":15667171,"avatar_url":"https://avatars.githubusercontent.com/u/15667171?v=3","gravatar_id":"","url":"https://api.github.com/users/odlgroup","html_url":"https://github.com/odlgroup","followers_url":"https://api.github.com/users/odlgroup/followers","following_url":"https://api.github.com/users/odlgroup/following{/other_user}","gists_url":"https://api.github.com/users/odlgroup/gists{/gist_id}","starred_url":"https://api.github.com/users/odlgroup/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/odlgroup/subscriptions","organizations_url":"https://api.github.com/users/odlgroup/orgs","repos_url":"https://api.github.com/users/odlgroup/repos","events_url":"https://api.github.com/users/odlgroup/events{/privacy}","received_events_url":"https://api.github.com/users/odlgroup/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/odlgroup/odl","description":"Operator Discretization Library","fork":false,"url":"https://api.github.com/repos/odlgroup/odl","forks_url":"https://api.github.com/repos/odlgroup/odl/forks","keys_url":"https://api.github.com/repos/odlgroup/odl/keys{/key_id}","collaborators_url":"https://api.github.com/repos/odlgroup/odl/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/odlgroup/odl/teams","hooks_url":"https://api.github.com/repos/odlgroup/odl/hooks","issue_events_url":"https://api.github.com/repos/odlgroup/odl/issues/events{/number}","events_url":"https://api.github.com/repos/odlgroup/odl/events","assignees_url":"https://api.github.com/repos/odlgroup/odl/assignees{/user}","branches_url":"https://api.github.com/repos/odlgroup/odl/branches{/branch}","tags_url":"https://api.github.com/repos/odlgroup/odl/tags","blobs_url":"https://api.github.com/repos/odlgroup/odl/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/odlgroup/odl/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/odlgroup/odl/git/refs{/sha}","trees_url":"https://api.github.com/repos/odlgroup/odl/git/trees{/sha}","statuses_url":"https://api.github.com/repos/odlgroup/odl/statuses/{sha}","languages_url":"https://api.github.com/repos/odlgroup/odl/languages","stargazers_url":"https://api.github.com/repos/odlgroup/odl/stargazers","contributors_url":"https://api.github.com/repos/odlgroup/odl/contributors","subscribers_url":"https://api.github.com/repos/odlgroup/odl/subscribers","subscription_url":"https://api.github.com/repos/odlgroup/odl/subscription","commits_url":"https://api.github.com/repos/odlgroup/odl/commits{/sha}","git_commits_url":"https://api.github.com/repos/odlgroup/odl/git/commits{/sha}","comments_url":"https://api.github.com/repos/odlgroup/odl/comments{/number}","issue_comment_url":"https://api.github.com/repos/odlgroup/odl/issues/comments{/number}","contents_url":"https://api.github.com/repos/odlgroup/odl/contents/{+path}","compare_url":"https://api.github.com/repos/odlgroup/odl/compare/{base}...{head}","merges_url":"https://api.github.com/repos/odlgroup/odl/merges","archive_url":"https://api.github.com/repos/odlgroup/odl/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/odlgroup/odl/downloads","issues_url":"https://api.github.com/repos/odlgroup/odl/issues{/number}","pulls_url":"https://api.github.com/repos/odlgroup/odl/pulls{/number}","milestones_url":"https://api.github.com/repos/odlgroup/odl/milestones{/number}","notifications_url":"https://api.github.com/repos/odlgroup/odl/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/odlgroup/odl/labels{/name}","releases_url":"https://api.github.com/repos/odlgroup/odl/releases{/id}","deployments_url":"https://api.github.com/repos/odlgroup/odl/deployments","created_at":"2015-11-05T07:57:55Z","updated_at":"2016-11-03T16:59:19Z","pushed_at":"2016-11-15T10:22:45Z","git_url":"git://github.com/odlgroup/odl.git","ssh_url":"git@github.com:odlgroup/odl.git","clone_url":"https://github.com/odlgroup/odl.git","svn_url":"https://github.com/odlgroup/odl","homepage":null,"size":33444,"stargazers_count":15,"watchers_count":15,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":7,"mirror_url":null,"open_issues_count":142,"forks":7,"open_issues":142,"watchers":15,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/716"},"html":{"href":"https://github.com/odlgroup/odl/pull/716"},"issue":{"href":"https://api.github.com/repos/odlgroup/odl/issues/716"},"comments":{"href":"https://api.github.com/repos/odlgroup/odl/issues/716/comments"},"review_comments":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/716/comments"},"review_comment":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/odlgroup/odl/pulls/716/commits"},"statuses":{"href":"https://api.github.com/repos/odlgroup/odl/statuses/2aa5b3136488bb56f1db9eeb71c06b3032c6869f"}}}}
{"actor":{"display_login":"kohr-h"}}