{"action":"created","comment":{"url":"https://api.github.com/repos/numenta/nupic/pulls/comments/36813352","id":36813352,"diff_hunk":"@@ -25,182 +25,272 @@\n import datetime\n \n \n+from nupic.data.fieldmeta import FieldMetaSpecial\n \n-class RecordStreamIface(object):\n-  \"\"\"This is the interface for the record input/output storage classes.\"\"\"\n \n-  __metaclass__ = ABCMeta\n \n+def _getFieldIndexBySpecial(fields, special):\n+  \"\"\" Return index of the field matching the field meta special value.\n \n-  def __init__(self):\n-    self._sequenceId = -1\n+  :param fields: sequence of nupic.data.fieldmeta.FieldMetaInfo objects\n+    representing the fields of a stream\n+  :param special: one of the special field attribute values from\n+    nupic.data.fieldmeta.FieldMetaSpecial\n \n+  :returns: first zero-based index of the field tagged with the target field\n+    meta special attribute; None if no such field\n+  \"\"\"\n+  for i, field in enumerate(fields):\n+    if field.special == special:\n+      return i\n+  return None\n \n-  @abstractmethod\n-  def close(self):\n-    \"\"\" Close the stream\n-    \"\"\"\n \n \n-  def rewind(self):\n-    \"\"\"Put us back at the beginning of the file again) \"\"\"\n+\n+class RecordDictEncoder(object):\n+  \"\"\"Encodes metric data input rows as dicts for consumption by OPF models. See\n+  the `RecordDictEncoder.encode` method for more details.\n+  \"\"\"\n+\n+\n+  def __init__(self, fields, aggregationPeriod=None):\n+    \"\"\"\n+    :param fields: sequence of nupic.data.fieldmeta.FieldMetaInfo objects\n+      corresponding to fields in input rows.\n+    :param dict aggregationPeriod: aggregation period of the record stream as a\n+      dict containing 'months' and 'seconds'. The months is always an integer\n+      and seconds is a floating point. Only one is allowed to be non-zero at a\n+      time. If there is no aggregation associated with the stream, pass None.\n+      Typically, a raw file or hbase stream will NOT have any aggregation info,\n+      but subclasses of RecordStreamIface, like StreamReader, will and will\n+      provide the aggregation period. This is used by the encode method to\n+      assign a record number to a record given its timestamp and the aggregation\n+      interval.\n+    \"\"\"\n+    self._fields = fields\n+    self._aggregationPeriod = aggregationPeriod\n+\n     self._sequenceId = -1\n \n+    self._fieldNames = tuple(f.name for f in fields)\n \n-  @abstractmethod\n-  def getNextRecord(self, useCache=True):\n-    \"\"\"Returns next available data record from the storage. If useCache is\n-    False, then don't read ahead and don't cache any records.\n-    \n-    Raises nupic.support.exceptions.StreamDisappearedError if stream\n-    disappears (e.g., gets garbage-collected).\n-    \n-    retval: a data row (a list or tuple) if available; None, if no more records\n-             in the table (End of Stream - EOS); empty sequence (list or tuple)\n-             when timing out while waiting for the next record.\n+    self._categoryFieldIndex = _getFieldIndexBySpecial(\n+      fields,\n+      FieldMetaSpecial.category)\n+\n+    self._resetFieldIndex = _getFieldIndexBySpecial(\n+      fields,\n+      FieldMetaSpecial.reset)\n+\n+    self._sequenceFieldIndex = _getFieldIndexBySpecial(\n+      fields,\n+      FieldMetaSpecial.sequence)\n+\n+    self._timestampFieldIndex = _getFieldIndexBySpecial(\n+      fields,\n+      FieldMetaSpecial.timestamp)\n+\n+    self._learningFieldIndex = _getFieldIndexBySpecial(\n+      fields,\n+      FieldMetaSpecial.learning)\n+\n+\n+  @property\n+  def fields(self):\n+    \"\"\"\n+    :returns: sequence of nupic.data.fieldmeta.FieldMetaInfo objects\n+      corresponding to fields in input rows.\n     \"\"\"\n+    return self._fields\n \n \n-  def getNextRecordDict(self):\n-    \"\"\"Returns next available data record from the storage as a dict, with the\n+  def rewind(self):\n+    \"\"\"Put us back at the beginning of the file again \"\"\"\n+    self._sequenceId = -1\n+\n+\n+  def encode(self, inputRow):\n+    \"\"\"Encodes the given input row as a dict, with the\n     keys being the field names. This also adds in some meta fields:\n       '_category': The value from the category field (if any)\n       '_reset': True if the reset field was True (if any)\n       '_sequenceId': the value from the sequenceId field (if any)\n-    \n+\n+    :param inputRow: sequence of values corresponding to a single input metric\n+      data row\n+    :rtype: dict\n     \"\"\"\n-    \n-    values = self.getNextRecord()\n-    if values is None:\n+\n+    if inputRow is None:\n       return None\n-    \n-    if not values:\n+\n+    if not inputRow:\n       return dict()\n-    \n+\n     # Create the return dict\n-    result = dict(zip(self.getFieldNames(), values))\n-    \n-    # Add in the special fields\n-    catIdx = self.getCategoryFieldIdx()\n-    resetIdx = self.getResetFieldIdx()\n-    sequenceIdx = self.getSequenceIdFieldIdx()\n-    timeIdx = self.getTimestampFieldIdx()\n-    learningIdx = self.getLearningFieldIdx()\n+    result = dict(zip(self._fieldNames, inputRow))\n \n-    if catIdx is not None:\n+    # Add in the special fields\n+    if self._categoryFieldIndex is not None:\n       # category value can be an int or a list\n-      if isinstance(values[catIdx], int):\n-        result['_category'] = [values[catIdx]]\n+      if isinstance(inputRow[self._categoryFieldIndex], int):\n+        result['_category'] = [inputRow[self._categoryFieldIndex]]\n       else:\n-        result['_category'] = values[catIdx] if values[catIdx] else [None]\n+        result['_category'] = (inputRow[self._categoryFieldIndex]\n+                               if inputRow[self._categoryFieldIndex]\n+                               else [None])\n     else:\n       result['_category'] = [None]\n \n-    if resetIdx is not None:\n-      result['_reset'] = int(bool(values[resetIdx]))\n+    if self._resetFieldIndex is not None:\n+      result['_reset'] = int(bool(inputRow[self._resetFieldIndex]))\n     else:\n       result['_reset'] = 0\n-      \n-    if learningIdx is not None:\n-      result['_learning'] = int(bool(values[learningIdx]))\n-      \n+\n+    if self._learningFieldIndex is not None:\n+      result['_learning'] = int(bool(inputRow[self._learningFieldIndex]))\n+\n     result['_timestampRecordIdx'] = None\n-    if timeIdx is not None:\n-      result['_timestamp'] = values[timeIdx]      \n+    if self._timestampFieldIndex is not None:\n+      result['_timestamp'] = inputRow[self._timestampFieldIndex]\n       # Compute the record index based on timestamp\n       result['_timestampRecordIdx'] = self._computeTimestampRecordIdx(\n-                                                        values[timeIdx])\n+        inputRow[self._timestampFieldIndex])\n     else:\n       result['_timestamp'] = None\n-    \n+\n     # -----------------------------------------------------------------------\n     # Figure out the sequence ID\n-    hasReset = resetIdx is not None\n-    hasSequenceId = sequenceIdx is not None\n+    hasReset = self._resetFieldIndex is not None\n+    hasSequenceId = self._sequenceFieldIndex is not None\n     if hasReset and not hasSequenceId:\n       # Reset only\n       if result['_reset']:\n         try:\n           self._sequenceId += 1\n         except:\n-          import pdb; pdb.set_trace() \n+          import pdb; pdb.set_trace()\n       sequenceId = self._sequenceId\n-      \n+\n     elif not hasReset and hasSequenceId:\n-      sequenceId = values[sequenceIdx]\n+      sequenceId = inputRow[self._sequenceFieldIndex]\n       result['_reset'] = int(sequenceId != self._sequenceId)\n       self._sequenceId = sequenceId\n-      \n+\n     elif hasReset and hasSequenceId:\n-      sequenceId = values[sequenceIdx]\n-      \n+      sequenceId = inputRow[self._sequenceFieldIndex]\n+\n     else:\n       sequenceId = 0\n-      \n+\n     if sequenceId is not None:\n       result['_sequenceId'] = hash(sequenceId)\n     else:\n       result['_sequenceId'] = None\n-    \n+\n     return result\n \n \n   def _computeTimestampRecordIdx(self, recordTS):\n     \"\"\" Give the timestamp of a record (a datetime object), compute the record's\n-    timestamp index - this is the timestamp divided by the aggregation period. \n-    \n-    \n+    timestamp index - this is the timestamp divided by the aggregation period.\n+\n+\n     Parameters:\n     ------------------------------------------------------------------------\n     recordTS:  datetime instance\n-    retval:    record timestamp index, or None if no aggregation period \n+    retval:    record timestamp index, or None if no aggregation period\n     \"\"\"\n-    \n-    aggPeriod = self.getAggregationMonthsAndSeconds()\n-    if aggPeriod is None:\n+\n+    if self._aggregationPeriod is None:\n       return None\n-    \n-    # Base record index on number of elapsed months if aggregation is in \n+\n+    # Base record index on number of elapsed months if aggregation is in\n     #  months\n-    if aggPeriod['months'] > 0:\n-      assert aggPeriod['seconds'] == 0\n-      result = \\\n-        int((recordTS.year * 12 + (recordTS.month-1)) / aggPeriod['months'])\n-        \n+    if self._aggregationPeriod['months'] > 0:\n+      assert self._aggregationPeriod['seconds'] == 0\n+      result = int(\n+        (recordTS.year * 12 + (recordTS.month-1)) /\n+        self._aggregationPeriod['months'])\n+\n     # Base record index on elapsed seconds\n-    elif aggPeriod['seconds'] > 0:\n+    elif self._aggregationPeriod['seconds'] > 0:\n       delta = recordTS - datetime.datetime(year=1, month=1, day=1)\n       deltaSecs = delta.days * 24 * 60 * 60   \\\n                 + delta.seconds               \\\n                 + delta.microseconds / 1000000.0\n-      result = int(deltaSecs / aggPeriod['seconds'])\n-    \n+      result = int(deltaSecs / self._aggregationPeriod['seconds'])\n+\n     else:\n       result = None\n-      \n+\n     return result\n \n \n-  def getAggregationMonthsAndSeconds(self):\n-    \"\"\" Returns the aggregation period of the record stream as a dict \n-    containing 'months' and 'seconds'. The months is always an integer and\n-    seconds is a floating point. Only one is allowed to be non-zero.  \n-    \n-    If there is no aggregation associated with the stream, returns None. \n-    \n-    Typically, a raw file or hbase stream will NOT have any aggregation info,\n-    but subclasses of RecordStreamIFace, like StreamReader, will and will\n-    return the aggregation period from this call. This call is used by the\n-    getNextRecordDict() method to assign a record number to a record given\n-    its timestamp and the aggregation interval\n-    \n-    Parameters:\n-    ------------------------------------------------------------------------\n-    retval: aggregationPeriod (as a dict) or None  \n-              'months': number of months in aggregation period\n-              'seconds': number of seconds in aggregation period (as a float)\n+\n+class RecordStreamIface(object):\n+  \"\"\"This is the interface for the record input/output storage classes.\"\"\"\n+\n+  __metaclass__ = ABCMeta\n+\n+\n+  def __init__(self, fields, aggregationPeriod=None):","path":"nupic/data/record_stream.py","position":313,"original_position":313,"commit_id":"223bb686618cbefb8f6ea6022ae7ae93ea2c3366","original_commit_id":"223bb686618cbefb8f6ea6022ae7ae93ea2c3366","user":{"login":"vitaly-krugl","id":1441066,"avatar_url":"https://avatars.githubusercontent.com/u/1441066?v=3","gravatar_id":"","url":"https://api.github.com/users/vitaly-krugl","html_url":"https://github.com/vitaly-krugl","followers_url":"https://api.github.com/users/vitaly-krugl/followers","following_url":"https://api.github.com/users/vitaly-krugl/following{/other_user}","gists_url":"https://api.github.com/users/vitaly-krugl/gists{/gist_id}","starred_url":"https://api.github.com/users/vitaly-krugl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vitaly-krugl/subscriptions","organizations_url":"https://api.github.com/users/vitaly-krugl/orgs","repos_url":"https://api.github.com/users/vitaly-krugl/repos","events_url":"https://api.github.com/users/vitaly-krugl/events{/privacy}","received_events_url":"https://api.github.com/users/vitaly-krugl/received_events","type":"User","site_admin":false},"body":"Note that the args in the `RecordStreamIface` constitute a change in its interface versus `def __init__(self)`. The legacy code was a bit backwards in that it depended on abstract methods to query this **static** information on demand each time it was needed (and it needed these values) instead of simply getting it via constructor as is typical.","created_at":"2015-08-11T23:45:25Z","updated_at":"2015-08-11T23:45:25Z","html_url":"https://github.com/numenta/nupic/pull/2420#discussion_r36813352","pull_request_url":"https://api.github.com/repos/numenta/nupic/pulls/2420","_links":{"self":{"href":"https://api.github.com/repos/numenta/nupic/pulls/comments/36813352"},"html":{"href":"https://github.com/numenta/nupic/pull/2420#discussion_r36813352"},"pull_request":{"href":"https://api.github.com/repos/numenta/nupic/pulls/2420"}}},"pull_request":{"url":"https://api.github.com/repos/numenta/nupic/pulls/2420","id":42186603,"html_url":"https://github.com/numenta/nupic/pull/2420","diff_url":"https://github.com/numenta/nupic/pull/2420.diff","patch_url":"https://github.com/numenta/nupic/pull/2420.patch","issue_url":"https://api.github.com/repos/numenta/nupic/issues/2420","number":2420,"state":"open","locked":false,"title":"Split out the Record-dict encoding logic from RecordStreamIface into a separate class.","user":{"login":"vitaly-krugl","id":1441066,"avatar_url":"https://avatars.githubusercontent.com/u/1441066?v=3","gravatar_id":"","url":"https://api.github.com/users/vitaly-krugl","html_url":"https://github.com/vitaly-krugl","followers_url":"https://api.github.com/users/vitaly-krugl/followers","following_url":"https://api.github.com/users/vitaly-krugl/following{/other_user}","gists_url":"https://api.github.com/users/vitaly-krugl/gists{/gist_id}","starred_url":"https://api.github.com/users/vitaly-krugl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vitaly-krugl/subscriptions","organizations_url":"https://api.github.com/users/vitaly-krugl/orgs","repos_url":"https://api.github.com/users/vitaly-krugl/repos","events_url":"https://api.github.com/users/vitaly-krugl/events{/privacy}","received_events_url":"https://api.github.com/users/vitaly-krugl/received_events","type":"User","site_admin":false},"body":"NOTE: this is a preview PR; it doesn't include tests, yet, and no issue has been filed.\r\n\r\nSplit out the Record-dict encoding logic from `RecordStreamIface` into a separate class so that it may be used by clamodel apps that don't need the rest of `RecordStreamIface`'s abstract-base-class baggage.\r\n\r\nPass fieldmetainfo list to RecordStreamIface instead of querying the derived class every time these static values are needed.\r\nRemove unnecessary implementations of several methods from derived StreamReader class, since an equivalent implementation already exists in `RecordStreamIface`.\r\n\r\nReplace use of hard-coded special attribute values with constants from `FieldMetaSpecial`.\r\n\r\nResolved most pylint issues in affected modules.\r\n\r\nFixed getFields validation tests: assert comparison of sequences instead of expecting the values wrapped in a list type explicitly.","created_at":"2015-08-11T22:37:40Z","updated_at":"2015-08-11T23:45:25Z","closed_at":null,"merged_at":null,"merge_commit_sha":"769a0defcabe118dc90f4ba337184abadfdad38e","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/numenta/nupic/pulls/2420/commits","review_comments_url":"https://api.github.com/repos/numenta/nupic/pulls/2420/comments","review_comment_url":"https://api.github.com/repos/numenta/nupic/pulls/comments{/number}","comments_url":"https://api.github.com/repos/numenta/nupic/issues/2420/comments","statuses_url":"https://api.github.com/repos/numenta/nupic/statuses/223bb686618cbefb8f6ea6022ae7ae93ea2c3366","head":{"label":"vitaly-krugl:record-dict-encoder","ref":"record-dict-encoder","sha":"223bb686618cbefb8f6ea6022ae7ae93ea2c3366","user":{"login":"vitaly-krugl","id":1441066,"avatar_url":"https://avatars.githubusercontent.com/u/1441066?v=3","gravatar_id":"","url":"https://api.github.com/users/vitaly-krugl","html_url":"https://github.com/vitaly-krugl","followers_url":"https://api.github.com/users/vitaly-krugl/followers","following_url":"https://api.github.com/users/vitaly-krugl/following{/other_user}","gists_url":"https://api.github.com/users/vitaly-krugl/gists{/gist_id}","starred_url":"https://api.github.com/users/vitaly-krugl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vitaly-krugl/subscriptions","organizations_url":"https://api.github.com/users/vitaly-krugl/orgs","repos_url":"https://api.github.com/users/vitaly-krugl/repos","events_url":"https://api.github.com/users/vitaly-krugl/events{/privacy}","received_events_url":"https://api.github.com/users/vitaly-krugl/received_events","type":"User","site_admin":false},"repo":{"id":13336614,"name":"nupic","full_name":"vitaly-krugl/nupic","owner":{"login":"vitaly-krugl","id":1441066,"avatar_url":"https://avatars.githubusercontent.com/u/1441066?v=3","gravatar_id":"","url":"https://api.github.com/users/vitaly-krugl","html_url":"https://github.com/vitaly-krugl","followers_url":"https://api.github.com/users/vitaly-krugl/followers","following_url":"https://api.github.com/users/vitaly-krugl/following{/other_user}","gists_url":"https://api.github.com/users/vitaly-krugl/gists{/gist_id}","starred_url":"https://api.github.com/users/vitaly-krugl/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/vitaly-krugl/subscriptions","organizations_url":"https://api.github.com/users/vitaly-krugl/orgs","repos_url":"https://api.github.com/users/vitaly-krugl/repos","events_url":"https://api.github.com/users/vitaly-krugl/events{/privacy}","received_events_url":"https://api.github.com/users/vitaly-krugl/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/vitaly-krugl/nupic","description":"Numenta Platform for Intelligent Computing","fork":true,"url":"https://api.github.com/repos/vitaly-krugl/nupic","forks_url":"https://api.github.com/repos/vitaly-krugl/nupic/forks","keys_url":"https://api.github.com/repos/vitaly-krugl/nupic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/vitaly-krugl/nupic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/vitaly-krugl/nupic/teams","hooks_url":"https://api.github.com/repos/vitaly-krugl/nupic/hooks","issue_events_url":"https://api.github.com/repos/vitaly-krugl/nupic/issues/events{/number}","events_url":"https://api.github.com/repos/vitaly-krugl/nupic/events","assignees_url":"https://api.github.com/repos/vitaly-krugl/nupic/assignees{/user}","branches_url":"https://api.github.com/repos/vitaly-krugl/nupic/branches{/branch}","tags_url":"https://api.github.com/repos/vitaly-krugl/nupic/tags","blobs_url":"https://api.github.com/repos/vitaly-krugl/nupic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/vitaly-krugl/nupic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/vitaly-krugl/nupic/git/refs{/sha}","trees_url":"https://api.github.com/repos/vitaly-krugl/nupic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/vitaly-krugl/nupic/statuses/{sha}","languages_url":"https://api.github.com/repos/vitaly-krugl/nupic/languages","stargazers_url":"https://api.github.com/repos/vitaly-krugl/nupic/stargazers","contributors_url":"https://api.github.com/repos/vitaly-krugl/nupic/contributors","subscribers_url":"https://api.github.com/repos/vitaly-krugl/nupic/subscribers","subscription_url":"https://api.github.com/repos/vitaly-krugl/nupic/subscription","commits_url":"https://api.github.com/repos/vitaly-krugl/nupic/commits{/sha}","git_commits_url":"https://api.github.com/repos/vitaly-krugl/nupic/git/commits{/sha}","comments_url":"https://api.github.com/repos/vitaly-krugl/nupic/comments{/number}","issue_comment_url":"https://api.github.com/repos/vitaly-krugl/nupic/issues/comments{/number}","contents_url":"https://api.github.com/repos/vitaly-krugl/nupic/contents/{+path}","compare_url":"https://api.github.com/repos/vitaly-krugl/nupic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/vitaly-krugl/nupic/merges","archive_url":"https://api.github.com/repos/vitaly-krugl/nupic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/vitaly-krugl/nupic/downloads","issues_url":"https://api.github.com/repos/vitaly-krugl/nupic/issues{/number}","pulls_url":"https://api.github.com/repos/vitaly-krugl/nupic/pulls{/number}","milestones_url":"https://api.github.com/repos/vitaly-krugl/nupic/milestones{/number}","notifications_url":"https://api.github.com/repos/vitaly-krugl/nupic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/vitaly-krugl/nupic/labels{/name}","releases_url":"https://api.github.com/repos/vitaly-krugl/nupic/releases{/id}","created_at":"2013-10-04T23:01:31Z","updated_at":"2014-11-25T22:14:59Z","pushed_at":"2015-08-11T23:24:04Z","git_url":"git://github.com/vitaly-krugl/nupic.git","ssh_url":"git@github.com:vitaly-krugl/nupic.git","clone_url":"https://github.com/vitaly-krugl/nupic.git","svn_url":"https://github.com/vitaly-krugl/nupic","homepage":"http://numenta.org/","size":210998,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"numenta:master","ref":"master","sha":"67f9dff52e767c58ca259293066e0970dfb70271","user":{"login":"numenta","id":1039191,"avatar_url":"https://avatars.githubusercontent.com/u/1039191?v=3","gravatar_id":"","url":"https://api.github.com/users/numenta","html_url":"https://github.com/numenta","followers_url":"https://api.github.com/users/numenta/followers","following_url":"https://api.github.com/users/numenta/following{/other_user}","gists_url":"https://api.github.com/users/numenta/gists{/gist_id}","starred_url":"https://api.github.com/users/numenta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/numenta/subscriptions","organizations_url":"https://api.github.com/users/numenta/orgs","repos_url":"https://api.github.com/users/numenta/repos","events_url":"https://api.github.com/users/numenta/events{/privacy}","received_events_url":"https://api.github.com/users/numenta/received_events","type":"Organization","site_admin":false},"repo":{"id":9252533,"name":"nupic","full_name":"numenta/nupic","owner":{"login":"numenta","id":1039191,"avatar_url":"https://avatars.githubusercontent.com/u/1039191?v=3","gravatar_id":"","url":"https://api.github.com/users/numenta","html_url":"https://github.com/numenta","followers_url":"https://api.github.com/users/numenta/followers","following_url":"https://api.github.com/users/numenta/following{/other_user}","gists_url":"https://api.github.com/users/numenta/gists{/gist_id}","starred_url":"https://api.github.com/users/numenta/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/numenta/subscriptions","organizations_url":"https://api.github.com/users/numenta/orgs","repos_url":"https://api.github.com/users/numenta/repos","events_url":"https://api.github.com/users/numenta/events{/privacy}","received_events_url":"https://api.github.com/users/numenta/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/numenta/nupic","description":"Numenta Platform for Intelligent Computing: a brain-inspired machine intelligence platform, and biologically accurate neural network based on cortical learning algorithms.","fork":false,"url":"https://api.github.com/repos/numenta/nupic","forks_url":"https://api.github.com/repos/numenta/nupic/forks","keys_url":"https://api.github.com/repos/numenta/nupic/keys{/key_id}","collaborators_url":"https://api.github.com/repos/numenta/nupic/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/numenta/nupic/teams","hooks_url":"https://api.github.com/repos/numenta/nupic/hooks","issue_events_url":"https://api.github.com/repos/numenta/nupic/issues/events{/number}","events_url":"https://api.github.com/repos/numenta/nupic/events","assignees_url":"https://api.github.com/repos/numenta/nupic/assignees{/user}","branches_url":"https://api.github.com/repos/numenta/nupic/branches{/branch}","tags_url":"https://api.github.com/repos/numenta/nupic/tags","blobs_url":"https://api.github.com/repos/numenta/nupic/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/numenta/nupic/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/numenta/nupic/git/refs{/sha}","trees_url":"https://api.github.com/repos/numenta/nupic/git/trees{/sha}","statuses_url":"https://api.github.com/repos/numenta/nupic/statuses/{sha}","languages_url":"https://api.github.com/repos/numenta/nupic/languages","stargazers_url":"https://api.github.com/repos/numenta/nupic/stargazers","contributors_url":"https://api.github.com/repos/numenta/nupic/contributors","subscribers_url":"https://api.github.com/repos/numenta/nupic/subscribers","subscription_url":"https://api.github.com/repos/numenta/nupic/subscription","commits_url":"https://api.github.com/repos/numenta/nupic/commits{/sha}","git_commits_url":"https://api.github.com/repos/numenta/nupic/git/commits{/sha}","comments_url":"https://api.github.com/repos/numenta/nupic/comments{/number}","issue_comment_url":"https://api.github.com/repos/numenta/nupic/issues/comments{/number}","contents_url":"https://api.github.com/repos/numenta/nupic/contents/{+path}","compare_url":"https://api.github.com/repos/numenta/nupic/compare/{base}...{head}","merges_url":"https://api.github.com/repos/numenta/nupic/merges","archive_url":"https://api.github.com/repos/numenta/nupic/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/numenta/nupic/downloads","issues_url":"https://api.github.com/repos/numenta/nupic/issues{/number}","pulls_url":"https://api.github.com/repos/numenta/nupic/pulls{/number}","milestones_url":"https://api.github.com/repos/numenta/nupic/milestones{/number}","notifications_url":"https://api.github.com/repos/numenta/nupic/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/numenta/nupic/labels{/name}","releases_url":"https://api.github.com/repos/numenta/nupic/releases{/id}","created_at":"2013-04-05T23:14:27Z","updated_at":"2015-08-11T03:21:15Z","pushed_at":"2015-08-11T23:41:32Z","git_url":"git://github.com/numenta/nupic.git","ssh_url":"git@github.com:numenta/nupic.git","clone_url":"https://github.com/numenta/nupic.git","svn_url":"https://github.com/numenta/nupic","homepage":"http://numenta.org/","size":310360,"stargazers_count":3331,"watchers_count":3331,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":898,"mirror_url":null,"open_issues_count":420,"forks":898,"open_issues":420,"watchers":3331,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/numenta/nupic/pulls/2420"},"html":{"href":"https://github.com/numenta/nupic/pull/2420"},"issue":{"href":"https://api.github.com/repos/numenta/nupic/issues/2420"},"comments":{"href":"https://api.github.com/repos/numenta/nupic/issues/2420/comments"},"review_comments":{"href":"https://api.github.com/repos/numenta/nupic/pulls/2420/comments"},"review_comment":{"href":"https://api.github.com/repos/numenta/nupic/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/numenta/nupic/pulls/2420/commits"},"statuses":{"href":"https://api.github.com/repos/numenta/nupic/statuses/223bb686618cbefb8f6ea6022ae7ae93ea2c3366"}}}}