{"action":"created","comment":{"url":"https://api.github.com/repos/lsst/qserv/pulls/comments/40718986","id":40718986,"diff_hunk":"@@ -0,0 +1,848 @@\n+/*\n+ * LSST Data Management System\n+ * Copyright 2015 AURA/LSST.\n+ *\n+ * This product includes software developed by the\n+ * LSST Project (http://www.lsst.org/).\n+ *\n+ * This program 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+ * This program 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 LSST License Statement and\n+ * the GNU General Public License along with this program.  If not,\n+ * see <https://www.lsstcorp.org/LegalNotices/>.\n+ */\n+\n+// Class header\n+#include \"css/CssAccess.h\"\n+\n+// System headers\n+#include <fstream>\n+#include <sstream>\n+\n+// Third-party headers\n+#include \"boost/property_tree/ptree.hpp\"\n+#include \"boost/property_tree/json_parser.hpp\"\n+\n+// LSST headers\n+#include \"lsst/log/Log.h\"\n+\n+// Qserv headers\n+#include \"css/constants.h\"\n+#include \"css/CssError.h\"\n+#include \"css/EmptyChunks.h\"\n+#include \"css/KvInterface.h\"\n+#include \"css/KvInterfaceImplMem.h\"\n+#include \"util/IterableFormatter.h\"\n+\n+namespace {\n+\n+LOG_LOGGER _log = LOG_GET(\"lsst.qserv.css.CssAccess\");\n+\n+}\n+\n+namespace lsst {\n+namespace qserv {\n+namespace css {\n+\n+std::shared_ptr<CssAccess>\n+CssAccess::makeMemCss(std::string const& mapPath, std::string const& emptyChunkPath) {\n+    std::ifstream f(mapPath);\n+    if(f.fail()) {\n+        throw ConnError();\n+    }\n+    return CssAccess::makeMemCss(f, emptyChunkPath);\n+}\n+\n+std::shared_ptr<CssAccess>\n+CssAccess::makeMemCss(std::istream& mapStream, std::string const& emptyChunkPath) {\n+    return CssAccess::makeKvCss(std::make_shared<KvInterfaceImplMem>(mapStream),\n+                                emptyChunkPath);\n+}\n+\n+std::shared_ptr<CssAccess>\n+CssAccess::makeKvCss(std::shared_ptr<KvInterface> const& kv, std::string const& emptyChunkPath) {\n+    return std::shared_ptr<CssAccess>(new CssAccess(kv, std::make_shared<EmptyChunks>(emptyChunkPath)));\n+}\n+\n+\n+// Construct from KvInterface instance and empty chunk list instance\n+CssAccess::CssAccess(std::shared_ptr<KvInterface> const& kvInterface,\n+                     std::shared_ptr<EmptyChunks> const& emptyChunks,\n+                     std::string const& prefix)\n+    : _kvI(kvInterface), _emptyChunks(emptyChunks), _prefix(prefix) {\n+}\n+\n+int\n+CssAccess::cssVersion() {\n+    return VERSION;\n+}\n+\n+std::vector<std::string>\n+CssAccess::getDbNames() const {\n+    std::string p = _prefix + \"/DBS\";\n+    return _kvI->getChildren(p);\n+}\n+\n+bool\n+CssAccess::containsDb(std::string const& dbName) const {\n+    if (dbName.empty()) {\n+        LOGF(_log, LOG_LVL_DEBUG, \"Empty database name passed.\");\n+        return false;\n+    }\n+    std::string p = _prefix + \"/DBS/\" + dbName;\n+    bool ret = _kvI->exists(p);\n+    LOGF(_log, LOG_LVL_DEBUG, \"containsDb(%1%): %2%\" % dbName % ret);\n+    return ret;\n+}\n+\n+StripingParams\n+CssAccess::getDbStriping(std::string const& dbName) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"getDbStriping(%1%)\" % dbName);\n+    StripingParams striping;\n+    auto dbMap = _getSubkeys(_prefix + \"/DBS/\" + dbName, {\"partitioningId\"});\n+    auto const& partId = dbMap[\"partitioningId\"];\n+    if (partId.empty()) {\n+        // if database is not defined throw an exception, otherwise return default values\n+        _assertDbExists(dbName);\n+        return striping;\n+    }\n+\n+    // get all keys\n+    std::string pKey = _prefix + \"/PARTITIONING/_\" + partId;\n+    std::vector<std::string> subKeys{\"nStripes\", \"nSubStripes\", \"overlap\"};\n+    auto const keyMap = _getSubkeys(pKey, subKeys);\n+\n+    // fill the structure\n+    try {\n+        striping.partitioningId = std::stoi(partId);\n+        auto iter = keyMap.find(\"nStripes\");\n+        if (iter != keyMap.end()) {\n+            striping.stripes = std::stoi(iter->second);\n+        }\n+        iter = keyMap.find(\"nSubStripes\");\n+        if (iter != keyMap.end()) {\n+            striping.subStripes = std::stoi(iter->second);\n+        }\n+        iter = keyMap.find(\"overlap\");\n+        if (iter != keyMap.end()) {\n+            striping.overlap = std::stod(iter->second);\n+        }\n+    } catch (std::exception const& exc) {\n+        throw KeyValueError(pKey, \"one of the keys is not numeric: \" + std::string(exc.what()));\n+    }\n+\n+    return striping;\n+}\n+\n+void\n+CssAccess::createDb(std::string const& dbName,\n+                    StripingParams const& striping,\n+                    std::string const& storageClass,\n+                    std::string const& releaseStatus) {\n+    LOGF(_log, LOG_LVL_DEBUG, \"createDb(%1%)\" % dbName);\n+\n+    std::string partId;\n+    if (striping.stripes > 0) {\n+        // need to define partitioning structure\n+\n+        // create unique key for it and get its id\n+        std::string const pfxKey = _prefix + \"/PARTITIONING/_\";\n+        std::string const partKey = _kvI->create(pfxKey, \"\", true);\n+        partId = partKey.substr(pfxKey.size());\n+\n+        // store striping structure\n+        // TODO: add uuid if we want to use it\n+        std::map<std::string, std::string> stripMap{\n+            std::make_pair(\"nStripes\", std::to_string(striping.stripes)),\n+            std::make_pair(\"nSubStripes\", std::to_string(striping.subStripes)),\n+            std::make_pair(\"overlap\", std::to_string(striping.overlap))};\n+        _storePacked(partKey, stripMap);\n+    }\n+\n+    // TODO: add uuid if we want to use it\n+    std::map<std::string, std::string> dbMap{\n+        std::make_pair(\"releaseStatus\", releaseStatus),\n+        std::make_pair(\"storageClass\", storageClass)};\n+    if (not partId.empty()) {\n+        dbMap.insert(std::make_pair(\"partitioningId\", partId));\n+    }\n+\n+    std::string const dbKey = _prefix + \"/DBS/\" + dbName;\n+    _storePacked(dbKey, dbMap);\n+    _kvI->set(dbKey, \"READY\");\n+}\n+\n+void\n+CssAccess::createDbLike(std::string const& dbName,\n+                        std::string const& templateDbName) {\n+    LOGF(_log, LOG_LVL_DEBUG, \"createDbLike(%1%)\" % dbName);\n+\n+    std::vector<std::string> subKeys{\"partitioningId\", \"releaseStatus\", \"storageClass\"};\n+    auto dbMap = _getSubkeys(_prefix + \"/DBS/\" + templateDbName, subKeys);\n+    if (dbMap.empty()) {\n+        // nothing is found, check whether db exists\n+        _assertDbExists(templateDbName);\n+    }\n+\n+    // make new database with the copy of all parameters\n+    std::string const dbKey = _prefix + \"/DBS/\" + dbName;\n+    _storePacked(dbKey, dbMap);\n+    _kvI->set(dbKey, \"READY\");\n+}\n+\n+void\n+CssAccess::dropDb(std::string const& dbName) {\n+    LOGF(_log, LOG_LVL_DEBUG, \"dropDb(%1%)\" % dbName);\n+\n+    std::string key = _prefix + \"/DBS/\" + dbName;\n+\n+    // key is supposed to exist and key.json is optional\n+    try {\n+        LOGF(_log, LOG_LVL_DEBUG, \"dropDb: try to delete packed key: %s.json\" % key);\n+        _kvI->deleteKey(key + \".json\");\n+    } catch (NoSuchKey const& exc) {\n+        LOGF(_log, LOG_LVL_DEBUG, \"dropDb: packed key is not found\");\n+    }\n+    try {\n+        LOGF(_log, LOG_LVL_DEBUG, \"dropDb: try to delete regular key: %s\" % key);\n+        _kvI->deleteKey(key);\n+    } catch (NoSuchKey const& exc) {\n+        LOGF(_log, LOG_LVL_DEBUG, \"dropDb: regular key is not found\");\n+        throw NoSuchDb(dbName);\n+    }\n+}\n+\n+std::vector<std::string>\n+CssAccess::getTableNames(std::string const& dbName, bool readyOnly) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"getTableNames(%1%)\" % dbName);\n+\n+    std::string key = _prefix + \"/DBS/\" + dbName + \"/TABLES\";\n+    std::vector<std::string> names;\n+    try {\n+        names = _kvI->getChildren(key);\n+    } catch (NoSuchKey const& exc) {\n+        LOGF(_log, LOG_LVL_DEBUG, \"getTableNames: key is not found: %s\" % key);\n+        throw NoSuchDb(dbName);\n+    }\n+\n+    // remove names from packed keys\n+    auto it = std::remove_if(names.begin(), names.end(),\n+                             [](const std::string& name) {\n+                                return name.compare(name.size()-5, name.size(), \".json\") == 0; });\n+    names.erase(it, names.end());\n+\n+    if (readyOnly and not names.empty()) {\n+        // filter out names with status other than READY\n+        auto const tableStatuses = _getSubkeys(key, names);\n+        names.clear();\n+        for (auto& kv: tableStatuses) {\n+            if (kv.second == \"READY\") {\n+                names.push_back(kv.first);\n+            }\n+        }\n+    }\n+    return names;\n+}\n+\n+bool\n+CssAccess::containsTable(std::string const& dbName, std::string const& tableName, bool readyOnly) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"containsTable(%1%, %2%)\" % dbName % tableName);\n+\n+    std::string const key = _prefix + \"/DBS/\" + dbName + \"/TABLES/\" + tableName;\n+    // If key is not there pretend that its value is not \"READY\"\n+    std::string const val = _kvI->get(key, \"DOES_NOT_EXIST\");\n+    if (val == \"DOES_NOT_EXIST\") {\n+        // table key is not there at all, throw if database name is not good\n+        _assertDbExists(dbName);\n+        LOGF(_log, LOG_LVL_DEBUG, \"containsTable: key is not found: %1%\" % key);\n+        return false;\n+    }\n+    LOGF(_log, LOG_LVL_DEBUG, \"containsTable: key value: %1%\" % val);\n+    // if key value is not \"READY\" it likely means table is in the process\n+    // of being deleted, which is the same as if it does not exist\n+    if (readyOnly) return val == \"READY\";\n+    return true;\n+}\n+\n+std::string\n+CssAccess::getTableSchema(std::string const& dbName, std::string const& tableName) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"getTableSchema(%1%, %2%)\" % dbName % tableName);\n+\n+    std::string const tableKey = _prefix + \"/DBS/\" + dbName + \"/TABLES/\" + tableName;\n+    auto kvMap = _getSubkeys(tableKey, {\"schema\"});\n+    auto const schema = kvMap[\"schema\"];\n+    if (schema.empty()) {\n+        // check table key\n+        if (not _kvI->exists(tableKey)) throw NoSuchTable(dbName, tableName);\n+    }\n+    return schema;\n+}\n+\n+MatchTableParams\n+CssAccess::getMatchTableParams(std::string const& dbName,\n+                               std::string const& tableName) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"getMatchTableParams(%1%, %2%)\" % dbName % tableName);\n+\n+    std::string const tableKey = _prefix + \"/DBS/\" + dbName + \"/TABLES/\" + tableName;\n+\n+    MatchTableParams params;\n+\n+    std::vector<std::string> subKeys{\"dirTable1\", \"dirColName1\", \"dirTable2\", \"dirColName2\", \"flagColName\"};\n+    auto paramMap = _getSubkeys(tableKey + \"/match\", subKeys);\n+    if (paramMap.empty()) {\n+        // check table key\n+        if (not _kvI->exists(tableKey)) throw NoSuchTable(dbName, tableName);\n+        return params;\n+    }\n+\n+    // fill the structure\n+    params.dirTable1 = paramMap[\"dirTable1\"];\n+    params.dirColName1 = paramMap[\"dirColName1\"];\n+    params.dirTable2 = paramMap[\"dirTable2\"];\n+    params.dirColName2 = paramMap[\"dirColName2\"];\n+    params.flagColName = paramMap[\"flagColName\"];\n+\n+    return params;\n+}\n+\n+PartTableParams\n+CssAccess::getPartTableParams(std::string const& dbName,\n+                              std::string const& tableName) const {\n+    LOGF(_log, LOG_LVL_DEBUG, \"getPartTableParams(%1%, %2%)\" % dbName % tableName);\n+\n+    std::string const tableKey = _prefix + \"/DBS/\" + dbName + \"/TABLES/\" + tableName;\n+\n+    PartTableParams params;\n+\n+    std::vector<std::string> subKeys{\"partitioning\", \"partitioning.json\", \"partitioning/subChunks\",\n+        \"partitioning/dirDb\", \"partitioning/dirTable\", \"partitioning/dirColName\", \"partitioning/latColName\",\n+        \"partitioning/lonColName\", \"partitioning/overlap\", \"partitioning/secIndexColName\"};\n+    auto paramMap = _getSubkeys(tableKey, subKeys);\n+    if (paramMap.empty()) {\n+        // check table key\n+        if (not _kvI->exists(tableKey)) throw NoSuchTable(dbName, tableName);\n+        return params;\n+    }\n+\n+    // fill the structure\n+    params.dirDb = paramMap[\"partitioning/dirDb\"];\n+    params.dirTable = paramMap[\"partitioning/dirTable\"];\n+    params.dirColName = paramMap[\"partitioning/dirColName\"];\n+    params.latColName = paramMap[\"partitioning/latColName\"];\n+    params.lonColName = paramMap[\"partitioning/lonColName\"];\n+    params.partitioned =\n+            paramMap.count(\"partitioning\") + paramMap.count(\"partitioning.json\") > 0;","path":"core/modules/css/CssAccess.cc","position":342,"original_position":342,"commit_id":"a505f43b46275d1314f083ea1e988e0efe98bee0","original_commit_id":"a505f43b46275d1314f083ea1e988e0efe98bee0","user":{"login":"jgates108","id":6558057,"avatar_url":"https://avatars.githubusercontent.com/u/6558057?v=3","gravatar_id":"","url":"https://api.github.com/users/jgates108","html_url":"https://github.com/jgates108","followers_url":"https://api.github.com/users/jgates108/followers","following_url":"https://api.github.com/users/jgates108/following{/other_user}","gists_url":"https://api.github.com/users/jgates108/gists{/gist_id}","starred_url":"https://api.github.com/users/jgates108/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jgates108/subscriptions","organizations_url":"https://api.github.com/users/jgates108/orgs","repos_url":"https://api.github.com/users/jgates108/repos","events_url":"https://api.github.com/users/jgates108/events{/privacy}","received_events_url":"https://api.github.com/users/jgates108/received_events","type":"User","site_admin":false},"body":"I don't think this line needs to be split.","created_at":"2015-09-29T19:42:47Z","updated_at":"2015-09-29T19:42:47Z","html_url":"https://github.com/lsst/qserv/pull/136#discussion_r40718986","pull_request_url":"https://api.github.com/repos/lsst/qserv/pulls/136","_links":{"self":{"href":"https://api.github.com/repos/lsst/qserv/pulls/comments/40718986"},"html":{"href":"https://github.com/lsst/qserv/pull/136#discussion_r40718986"},"pull_request":{"href":"https://api.github.com/repos/lsst/qserv/pulls/136"}}},"pull_request":{"url":"https://api.github.com/repos/lsst/qserv/pulls/136","id":46119940,"html_url":"https://github.com/lsst/qserv/pull/136","diff_url":"https://github.com/lsst/qserv/pull/136.diff","patch_url":"https://github.com/lsst/qserv/pull/136.patch","issue_url":"https://api.github.com/repos/lsst/qserv/issues/136","number":136,"state":"open","locked":false,"title":"Add CssAccess class providing new interface for CSS (DM-2733)","user":{"login":"andy-z","id":9602964,"avatar_url":"https://avatars.githubusercontent.com/u/9602964?v=3","gravatar_id":"","url":"https://api.github.com/users/andy-z","html_url":"https://github.com/andy-z","followers_url":"https://api.github.com/users/andy-z/followers","following_url":"https://api.github.com/users/andy-z/following{/other_user}","gists_url":"https://api.github.com/users/andy-z/gists{/gist_id}","starred_url":"https://api.github.com/users/andy-z/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/andy-z/subscriptions","organizations_url":"https://api.github.com/users/andy-z/orgs","repos_url":"https://api.github.com/users/andy-z/repos","events_url":"https://api.github.com/users/andy-z/events{/privacy}","received_events_url":"https://api.github.com/users/andy-z/received_events","type":"User","site_admin":false},"body":"This new class replaces Facade and has additional methods for update operations on CSS. Many methods of Facade that returned individual bits were replaced with methods returning combined info (like TableParams, PartTableParams, etc.) to keep data consistent. Unit tests for practically all methods of CssAccess are added as well.\r\n\r\nCzar is now using new CssAccess class, Facade has been removed. Passess all unit tests and integration tests.\r\n\r\nExtend KvInterface API to support operations needed by CssAccess.","created_at":"2015-09-28T04:39:02Z","updated_at":"2015-09-29T19:42:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f54083fe32cd71b6a317748123839a191d8751c2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/lsst/qserv/pulls/136/commits","review_comments_url":"https://api.github.com/repos/lsst/qserv/pulls/136/comments","review_comment_url":"https://api.github.com/repos/lsst/qserv/pulls/comments{/number}","comments_url":"https://api.github.com/repos/lsst/qserv/issues/136/comments","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/a505f43b46275d1314f083ea1e988e0efe98bee0","head":{"label":"lsst:tickets/DM-2733","ref":"tickets/DM-2733","sha":"a505f43b46275d1314f083ea1e988e0efe98bee0","user":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"repo":{"id":23013460,"name":"qserv","full_name":"lsst/qserv","owner":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lsst/qserv","description":"LSST Query Services","fork":false,"url":"https://api.github.com/repos/lsst/qserv","forks_url":"https://api.github.com/repos/lsst/qserv/forks","keys_url":"https://api.github.com/repos/lsst/qserv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lsst/qserv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lsst/qserv/teams","hooks_url":"https://api.github.com/repos/lsst/qserv/hooks","issue_events_url":"https://api.github.com/repos/lsst/qserv/issues/events{/number}","events_url":"https://api.github.com/repos/lsst/qserv/events","assignees_url":"https://api.github.com/repos/lsst/qserv/assignees{/user}","branches_url":"https://api.github.com/repos/lsst/qserv/branches{/branch}","tags_url":"https://api.github.com/repos/lsst/qserv/tags","blobs_url":"https://api.github.com/repos/lsst/qserv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lsst/qserv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lsst/qserv/git/refs{/sha}","trees_url":"https://api.github.com/repos/lsst/qserv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/{sha}","languages_url":"https://api.github.com/repos/lsst/qserv/languages","stargazers_url":"https://api.github.com/repos/lsst/qserv/stargazers","contributors_url":"https://api.github.com/repos/lsst/qserv/contributors","subscribers_url":"https://api.github.com/repos/lsst/qserv/subscribers","subscription_url":"https://api.github.com/repos/lsst/qserv/subscription","commits_url":"https://api.github.com/repos/lsst/qserv/commits{/sha}","git_commits_url":"https://api.github.com/repos/lsst/qserv/git/commits{/sha}","comments_url":"https://api.github.com/repos/lsst/qserv/comments{/number}","issue_comment_url":"https://api.github.com/repos/lsst/qserv/issues/comments{/number}","contents_url":"https://api.github.com/repos/lsst/qserv/contents/{+path}","compare_url":"https://api.github.com/repos/lsst/qserv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lsst/qserv/merges","archive_url":"https://api.github.com/repos/lsst/qserv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lsst/qserv/downloads","issues_url":"https://api.github.com/repos/lsst/qserv/issues{/number}","pulls_url":"https://api.github.com/repos/lsst/qserv/pulls{/number}","milestones_url":"https://api.github.com/repos/lsst/qserv/milestones{/number}","notifications_url":"https://api.github.com/repos/lsst/qserv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lsst/qserv/labels{/name}","releases_url":"https://api.github.com/repos/lsst/qserv/releases{/id}","created_at":"2014-08-16T07:50:36Z","updated_at":"2015-05-08T02:01:15Z","pushed_at":"2015-09-29T18:35:17Z","git_url":"git://github.com/lsst/qserv.git","ssh_url":"git@github.com:lsst/qserv.git","clone_url":"https://github.com/lsst/qserv.git","svn_url":"https://github.com/lsst/qserv","homepage":"http://dm.lsst.org/","size":21658,"stargazers_count":4,"watchers_count":4,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":3,"forks":4,"open_issues":3,"watchers":4,"default_branch":"master"}},"base":{"label":"lsst:master","ref":"master","sha":"f1395665bcd6c0a23a472201ebb8e99dbce6627e","user":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"repo":{"id":23013460,"name":"qserv","full_name":"lsst/qserv","owner":{"login":"lsst","id":1134504,"avatar_url":"https://avatars.githubusercontent.com/u/1134504?v=3","gravatar_id":"","url":"https://api.github.com/users/lsst","html_url":"https://github.com/lsst","followers_url":"https://api.github.com/users/lsst/followers","following_url":"https://api.github.com/users/lsst/following{/other_user}","gists_url":"https://api.github.com/users/lsst/gists{/gist_id}","starred_url":"https://api.github.com/users/lsst/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lsst/subscriptions","organizations_url":"https://api.github.com/users/lsst/orgs","repos_url":"https://api.github.com/users/lsst/repos","events_url":"https://api.github.com/users/lsst/events{/privacy}","received_events_url":"https://api.github.com/users/lsst/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/lsst/qserv","description":"LSST Query Services","fork":false,"url":"https://api.github.com/repos/lsst/qserv","forks_url":"https://api.github.com/repos/lsst/qserv/forks","keys_url":"https://api.github.com/repos/lsst/qserv/keys{/key_id}","collaborators_url":"https://api.github.com/repos/lsst/qserv/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/lsst/qserv/teams","hooks_url":"https://api.github.com/repos/lsst/qserv/hooks","issue_events_url":"https://api.github.com/repos/lsst/qserv/issues/events{/number}","events_url":"https://api.github.com/repos/lsst/qserv/events","assignees_url":"https://api.github.com/repos/lsst/qserv/assignees{/user}","branches_url":"https://api.github.com/repos/lsst/qserv/branches{/branch}","tags_url":"https://api.github.com/repos/lsst/qserv/tags","blobs_url":"https://api.github.com/repos/lsst/qserv/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/lsst/qserv/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/lsst/qserv/git/refs{/sha}","trees_url":"https://api.github.com/repos/lsst/qserv/git/trees{/sha}","statuses_url":"https://api.github.com/repos/lsst/qserv/statuses/{sha}","languages_url":"https://api.github.com/repos/lsst/qserv/languages","stargazers_url":"https://api.github.com/repos/lsst/qserv/stargazers","contributors_url":"https://api.github.com/repos/lsst/qserv/contributors","subscribers_url":"https://api.github.com/repos/lsst/qserv/subscribers","subscription_url":"https://api.github.com/repos/lsst/qserv/subscription","commits_url":"https://api.github.com/repos/lsst/qserv/commits{/sha}","git_commits_url":"https://api.github.com/repos/lsst/qserv/git/commits{/sha}","comments_url":"https://api.github.com/repos/lsst/qserv/comments{/number}","issue_comment_url":"https://api.github.com/repos/lsst/qserv/issues/comments{/number}","contents_url":"https://api.github.com/repos/lsst/qserv/contents/{+path}","compare_url":"https://api.github.com/repos/lsst/qserv/compare/{base}...{head}","merges_url":"https://api.github.com/repos/lsst/qserv/merges","archive_url":"https://api.github.com/repos/lsst/qserv/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/lsst/qserv/downloads","issues_url":"https://api.github.com/repos/lsst/qserv/issues{/number}","pulls_url":"https://api.github.com/repos/lsst/qserv/pulls{/number}","milestones_url":"https://api.github.com/repos/lsst/qserv/milestones{/number}","notifications_url":"https://api.github.com/repos/lsst/qserv/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/lsst/qserv/labels{/name}","releases_url":"https://api.github.com/repos/lsst/qserv/releases{/id}","created_at":"2014-08-16T07:50:36Z","updated_at":"2015-05-08T02:01:15Z","pushed_at":"2015-09-29T18:35:17Z","git_url":"git://github.com/lsst/qserv.git","ssh_url":"git@github.com:lsst/qserv.git","clone_url":"https://github.com/lsst/qserv.git","svn_url":"https://github.com/lsst/qserv","homepage":"http://dm.lsst.org/","size":21658,"stargazers_count":4,"watchers_count":4,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":3,"forks":4,"open_issues":3,"watchers":4,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/lsst/qserv/pulls/136"},"html":{"href":"https://github.com/lsst/qserv/pull/136"},"issue":{"href":"https://api.github.com/repos/lsst/qserv/issues/136"},"comments":{"href":"https://api.github.com/repos/lsst/qserv/issues/136/comments"},"review_comments":{"href":"https://api.github.com/repos/lsst/qserv/pulls/136/comments"},"review_comment":{"href":"https://api.github.com/repos/lsst/qserv/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/lsst/qserv/pulls/136/commits"},"statuses":{"href":"https://api.github.com/repos/lsst/qserv/statuses/a505f43b46275d1314f083ea1e988e0efe98bee0"}}}}