{"action":"created","comment":{"url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments/42431124","id":42431124,"diff_hunk":"@@ -0,0 +1,552 @@\n+#!/usr/bin/env python\n+########################################################################\n+# :file:    dirac-dms-directory-sync\n+# :author:  Marko Petric\n+########################################################################\n+\"\"\"\n+Provides basic rsync functionality for DIRAC\n+\n+Syncs the source destination folder recursivly into the target destination\n+\n+If option --sync is used contend that is not in the source directory but is\n+only in the target directory will be deleted.\n+\"\"\"\n+\n+__RCSID__ = \"$Id$\"\n+\n+import os\n+import DIRAC\n+from DIRAC.Core.Base import Script\n+\n+Script.setUsageMessage( '\\n'.join( [ __doc__.split( '\\n' )[1],\n+                                     'Usage:',\n+                                     '  %s Source Destination' % Script.scriptName,\n+                                     ' ',\n+                                     ' e.g.: Download',\n+                                     '   %s LFN Path' % Script.scriptName,\n+                                     '  or Upload',\n+                                     '   %s Path LFN SE' % Script.scriptName,\n+                                     'Arguments:',\n+                                     '  LFN:           Logical File Name (Path to directory)',\n+                                     '  Path:          Local path to the file (Path to directory)',\n+                                     '  SE:            DIRAC Storage Element']\n+                                 )\n+                      )\n+\n+Script.registerSwitch( \"D\" , \"sync\" , \"Make target directory identical to source\" )\n+Script.registerSwitch( \"j:\" , \"parallel=\" , \"Multithreaded download and upload\" )\n+Script.parseCommandLine( ignoreErrors = False )\n+\n+args = Script.getPositionalArgs()\n+if len( args ) < 1 or len( args ) > 3:\n+  Script.showHelp()\n+\n+sync = False\n+parallel = 1\n+for switch in Script.getUnprocessedSwitches():\n+  if switch[0].lower() == \"s\" or switch[0].lower() == \"sync\":\n+    sync = True\n+  if switch[0].lower() == \"j\" or switch[0].lower() == \"parallel\":\n+    parallel = int(switch[1])\n+\n+\n+from DIRAC import S_OK, S_ERROR\n+from DIRAC import gConfig, gLogger\n+from DIRAC.Resources.Catalog.FileCatalog import FileCatalog\n+from DIRAC.Core.Utilities.List import sortList, breakListIntoChunks\n+from DIRAC.DataManagementSystem.Client.DataManager import DataManager\n+from DIRAC.Resources.Storage.StorageElement import StorageElement\n+from DIRAC.Core.Utilities.ReturnValues import returnSingleResult\n+\n+def getSetOfLocalDirectoriesAndFiles( path ):\n+  \"\"\"Return a set of all directories and subdirectories and a set of\n+  files contained therein for a given local path\n+  \"\"\"\n+\n+  fullPath = os.path.abspath(path)\n+\n+  if not os.path.isdir(fullPath):\n+    return S_ERROR(\"The path: \" + fullPath + \" does not exist!\")\n+\n+  directories = set()\n+  files = set()\n+\n+  for dirname, dirnames, filenames in os.walk(path):\n+  # add path to all subdirectories first.\n+    for subdirname in dirnames:\n+      fullSubdirname = os.path.join(dirname,subdirname)\n+      fullSubdirname = os.path.abspath(fullSubdirname)\n+      fullSubdirname = fullSubdirname.replace(fullPath,'').lstrip('/')\n+      directories.add(fullSubdirname)\n+  # add path to all filenames.\n+    for filename in filenames:\n+      fullFilename = os.path.join(dirname,filename)\n+      fullFilename = os.path.abspath(fullFilename)\n+      fullFilename = fullFilename.replace(fullPath,'').lstrip('/')\n+      fileSize = os.path.getsize(fullPath + \"/\" +  fullFilename)\n+      if fileSize > 0:\n+        files.add((fullFilename,long(fileSize)))\n+\n+  tree = {}\n+  tree[\"Directories\"]=directories\n+  tree[\"Files\"]=files\n+\n+  return S_OK(tree)\n+\n+def getSetOfRemoteSubDirectoriesAndFiles(path,fc,directories,files):\n+  \"\"\"\n+  Recursively traverses all the subdirectories of a directory and returns a set of directories and files\n+  \"\"\"\n+  result = fc.listDirectory(path)\n+  if result['OK']:\n+    if result['Value']['Successful']:\n+      for entry in result['Value']['Successful'][path]['Files']:\n+        size = result['Value']['Successful'][path]['Files'][entry]['MetaData']['Size']\n+        files.add((entry,size))\n+      for entry in result['Value']['Successful'][path]['SubDirs']:\n+        directories.add(entry)\n+        res = getSetOfRemoteSubDirectoriesAndFiles(entry,fc,directories,files)\n+        if not res['OK']:\n+          return S_ERROR('Error: ' + res['Message'])\n+      return S_OK()\n+    else:\n+      return S_ERROR(\"Error:\" + result['Message'])\n+  else:\n+    return S_ERROR(\"Error:\" + result['Message'])\n+\n+def getSetOfRemoteDirectoriesAndFiles(fc, path):\n+  \"\"\"\n+  Return a set of all directories and subdirectories and the therein contained files for a given LFN\n+  \"\"\"\n+  directories = set()\n+  files = set()\n+\n+  res = getSetOfRemoteSubDirectoriesAndFiles(path,fc,directories,files)\n+  if not res['OK']:\n+    return S_ERROR('Could not list remote directory: ' + res['Message'])\n+\n+  return_directories = set()\n+  return_files = set()\n+\n+  for myfile in files:\n+    return_files.add((myfile[0].replace(path,'').lstrip('/'),myfile[1]))\n+\n+  for mydirectory in directories:\n+    return_directories.add(mydirectory.replace(path,'').lstrip('/'))\n+\n+  tree = {}\n+  tree[\"Directories\"]=return_directories\n+  tree[\"Files\"]=return_files\n+\n+  return S_OK(tree)\n+\n+def isInFileCatalog(fc, path ):\n+  \"\"\"\n+  Check if the file is in the File Catalog\n+  \"\"\"\n+\n+  result = fc.listDirectory(path)\n+  if result['OK']:\n+    if result['Value']['Successful']:\n+      return S_OK()\n+    else:\n+      return S_ERROR()\n+  else:\n+    return S_ERROR()\n+\n+def getContentToSync(upload, fc, source_dir, dest_dir):\n+  \"\"\"\n+  Return list of files and directories to be create and deleted\n+  \"\"\"\n+\n+  if upload:\n+    res = getSetOfRemoteDirectoriesAndFiles(fc, dest_dir)\n+    if not res['OK']:\n+      return S_ERROR(res['Message'])\n+    to_dirs = res['Value']['Directories']\n+    to_files = res['Value']['Files']\n+\n+    res = getSetOfLocalDirectoriesAndFiles(source_dir)\n+    if not res['OK']:\n+      return S_ERROR(res['Message'])\n+    from_dirs = res['Value']['Directories']\n+    from_files = res['Value']['Files']\n+\n+  else:\n+    res = getSetOfLocalDirectoriesAndFiles(dest_dir)\n+    if not res['OK']:\n+      return S_ERROR(res['Message'])\n+    to_dirs = res['Value']['Directories']\n+    to_files = res['Value']['Files']\n+\n+    res = getSetOfRemoteDirectoriesAndFiles(fc, source_dir)\n+    if not res['OK']:\n+      return S_ERROR(res['Message'])\n+    from_dirs = res['Value']['Directories']\n+    from_files = res['Value']['Files']\n+\n+  #Create list of directories to delete\n+  dirs_delete = list(to_dirs - from_dirs)\n+  #Sort the list by depth of directory tree\n+  dirs_delete.sort(key = lambda s: -s.count('/'))\n+  #Create list of directories to create\n+  dirs_create = list(from_dirs - to_dirs)\n+  #Sort the list by depth of directory tree\n+  dirs_create.sort(key = lambda s: s.count('/'))\n+\n+  #Flatten the list of pairs (filename, size) to list of filename\n+  files_delete = [pair[0] for pair in list(to_files - from_files)]\n+  files_create = [pair[0] for pair in list(from_files - to_files)]\n+\n+  create = {}\n+  create[\"Directories\"] = dirs_create\n+  create[\"Files\"] = files_create\n+\n+  delete = {}\n+  delete[\"Directories\"] = dirs_delete\n+  delete[\"Files\"] = files_delete\n+\n+  tree = {}\n+  tree[\"Create\"]=create\n+  tree[\"Delete\"]=delete\n+\n+  return S_OK(tree)\n+\n+\n+def removeRemoteFiles(dm,lfns):\n+  \"\"\"\n+  Remove file from the catalog\n+  \"\"\"\n+  for lfnList in breakListIntoChunks( lfns, 100 ):\n+    res = dm.removeFile( lfnList )\n+    if not res['OK']:\n+      return S_ERROR( \"Failed to remove files:\" + lfnList + res['Message'] )\n+    else:\n+      return S_OK()\n+\n+\n+def uploadLocalFile(dm, lfn, localfile, storage):\n+  \"\"\"\n+  Upload a local file to a storage element\n+  \"\"\"\n+  res = dm.putAndRegister( lfn, localfile, storage, None )\n+  if not res['OK']:\n+    return S_ERROR( 'Error: failed to upload %s to %s' % ( lfn, storage ) )\n+  else:\n+    return S_OK( 'Successfully uploaded file to %s' % storage )\n+\n+def downloadRemoteFile(dm, lfn, destination):","path":"DataManagementSystem/scripts/dirac-dms-directory-sync.py","position":238,"original_position":238,"commit_id":"54aaf53506824e23461a425a262a6f55e6dad738","original_commit_id":"54aaf53506824e23461a425a262a6f55e6dad738","user":{"login":"atsareg","id":679892,"avatar_url":"https://avatars.githubusercontent.com/u/679892?v=3","gravatar_id":"","url":"https://api.github.com/users/atsareg","html_url":"https://github.com/atsareg","followers_url":"https://api.github.com/users/atsareg/followers","following_url":"https://api.github.com/users/atsareg/following{/other_user}","gists_url":"https://api.github.com/users/atsareg/gists{/gist_id}","starred_url":"https://api.github.com/users/atsareg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/atsareg/subscriptions","organizations_url":"https://api.github.com/users/atsareg/orgs","repos_url":"https://api.github.com/users/atsareg/repos","events_url":"https://api.github.com/users/atsareg/events{/privacy}","received_events_url":"https://api.github.com/users/atsareg/received_events","type":"User","site_admin":false},"body":"Why do you need a separate method for downloading/uploading which is exactly the same as the DataManager methods ?","created_at":"2015-10-19T21:54:58Z","updated_at":"2015-10-19T21:54:58Z","html_url":"https://github.com/DIRACGrid/DIRAC/pull/2652#discussion_r42431124","pull_request_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652","_links":{"self":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments/42431124"},"html":{"href":"https://github.com/DIRACGrid/DIRAC/pull/2652#discussion_r42431124"},"pull_request":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652"}}},"pull_request":{"url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652","id":47805126,"html_url":"https://github.com/DIRACGrid/DIRAC/pull/2652","diff_url":"https://github.com/DIRACGrid/DIRAC/pull/2652.diff","patch_url":"https://github.com/DIRACGrid/DIRAC/pull/2652.patch","issue_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2652","number":2652,"state":"open","locked":false,"title":"Rel v6r12: dirac-dms-mirror","user":{"login":"petricm","id":8983816,"avatar_url":"https://avatars.githubusercontent.com/u/8983816?v=3","gravatar_id":"","url":"https://api.github.com/users/petricm","html_url":"https://github.com/petricm","followers_url":"https://api.github.com/users/petricm/followers","following_url":"https://api.github.com/users/petricm/following{/other_user}","gists_url":"https://api.github.com/users/petricm/gists{/gist_id}","starred_url":"https://api.github.com/users/petricm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petricm/subscriptions","organizations_url":"https://api.github.com/users/petricm/orgs","repos_url":"https://api.github.com/users/petricm/repos","events_url":"https://api.github.com/users/petricm/events{/privacy}","received_events_url":"https://api.github.com/users/petricm/received_events","type":"User","site_admin":false},"body":"Added rsync like utility to recursively copy directory to and from SE, with the optional exact copy (delete files in target directory that are not in source directory).","created_at":"2015-10-15T15:50:14Z","updated_at":"2015-10-19T21:54:58Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e930181dba7521ed3c5b9b44ec13cc493e4e68b0","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652/commits","review_comments_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652/comments","review_comment_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments{/number}","comments_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2652/comments","statuses_url":"https://api.github.com/repos/DIRACGrid/DIRAC/statuses/54aaf53506824e23461a425a262a6f55e6dad738","head":{"label":"petricm:rel-v6r12","ref":"rel-v6r12","sha":"54aaf53506824e23461a425a262a6f55e6dad738","user":{"login":"petricm","id":8983816,"avatar_url":"https://avatars.githubusercontent.com/u/8983816?v=3","gravatar_id":"","url":"https://api.github.com/users/petricm","html_url":"https://github.com/petricm","followers_url":"https://api.github.com/users/petricm/followers","following_url":"https://api.github.com/users/petricm/following{/other_user}","gists_url":"https://api.github.com/users/petricm/gists{/gist_id}","starred_url":"https://api.github.com/users/petricm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petricm/subscriptions","organizations_url":"https://api.github.com/users/petricm/orgs","repos_url":"https://api.github.com/users/petricm/repos","events_url":"https://api.github.com/users/petricm/events{/privacy}","received_events_url":"https://api.github.com/users/petricm/received_events","type":"User","site_admin":false},"repo":{"id":43428433,"name":"DIRAC","full_name":"petricm/DIRAC","owner":{"login":"petricm","id":8983816,"avatar_url":"https://avatars.githubusercontent.com/u/8983816?v=3","gravatar_id":"","url":"https://api.github.com/users/petricm","html_url":"https://github.com/petricm","followers_url":"https://api.github.com/users/petricm/followers","following_url":"https://api.github.com/users/petricm/following{/other_user}","gists_url":"https://api.github.com/users/petricm/gists{/gist_id}","starred_url":"https://api.github.com/users/petricm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/petricm/subscriptions","organizations_url":"https://api.github.com/users/petricm/orgs","repos_url":"https://api.github.com/users/petricm/repos","events_url":"https://api.github.com/users/petricm/events{/privacy}","received_events_url":"https://api.github.com/users/petricm/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/petricm/DIRAC","description":"DIRAC Grid","fork":true,"url":"https://api.github.com/repos/petricm/DIRAC","forks_url":"https://api.github.com/repos/petricm/DIRAC/forks","keys_url":"https://api.github.com/repos/petricm/DIRAC/keys{/key_id}","collaborators_url":"https://api.github.com/repos/petricm/DIRAC/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/petricm/DIRAC/teams","hooks_url":"https://api.github.com/repos/petricm/DIRAC/hooks","issue_events_url":"https://api.github.com/repos/petricm/DIRAC/issues/events{/number}","events_url":"https://api.github.com/repos/petricm/DIRAC/events","assignees_url":"https://api.github.com/repos/petricm/DIRAC/assignees{/user}","branches_url":"https://api.github.com/repos/petricm/DIRAC/branches{/branch}","tags_url":"https://api.github.com/repos/petricm/DIRAC/tags","blobs_url":"https://api.github.com/repos/petricm/DIRAC/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/petricm/DIRAC/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/petricm/DIRAC/git/refs{/sha}","trees_url":"https://api.github.com/repos/petricm/DIRAC/git/trees{/sha}","statuses_url":"https://api.github.com/repos/petricm/DIRAC/statuses/{sha}","languages_url":"https://api.github.com/repos/petricm/DIRAC/languages","stargazers_url":"https://api.github.com/repos/petricm/DIRAC/stargazers","contributors_url":"https://api.github.com/repos/petricm/DIRAC/contributors","subscribers_url":"https://api.github.com/repos/petricm/DIRAC/subscribers","subscription_url":"https://api.github.com/repos/petricm/DIRAC/subscription","commits_url":"https://api.github.com/repos/petricm/DIRAC/commits{/sha}","git_commits_url":"https://api.github.com/repos/petricm/DIRAC/git/commits{/sha}","comments_url":"https://api.github.com/repos/petricm/DIRAC/comments{/number}","issue_comment_url":"https://api.github.com/repos/petricm/DIRAC/issues/comments{/number}","contents_url":"https://api.github.com/repos/petricm/DIRAC/contents/{+path}","compare_url":"https://api.github.com/repos/petricm/DIRAC/compare/{base}...{head}","merges_url":"https://api.github.com/repos/petricm/DIRAC/merges","archive_url":"https://api.github.com/repos/petricm/DIRAC/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/petricm/DIRAC/downloads","issues_url":"https://api.github.com/repos/petricm/DIRAC/issues{/number}","pulls_url":"https://api.github.com/repos/petricm/DIRAC/pulls{/number}","milestones_url":"https://api.github.com/repos/petricm/DIRAC/milestones{/number}","notifications_url":"https://api.github.com/repos/petricm/DIRAC/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/petricm/DIRAC/labels{/name}","releases_url":"https://api.github.com/repos/petricm/DIRAC/releases{/id}","created_at":"2015-09-30T11:09:15Z","updated_at":"2015-09-30T11:09:23Z","pushed_at":"2015-10-16T16:14:27Z","git_url":"git://github.com/petricm/DIRAC.git","ssh_url":"git@github.com:petricm/DIRAC.git","clone_url":"https://github.com/petricm/DIRAC.git","svn_url":"https://github.com/petricm/DIRAC","homepage":"http://diracgrid.org","size":27946,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"integration"}},"base":{"label":"DIRACGrid:rel-v6r12","ref":"rel-v6r12","sha":"dd7dc946e84d65bfc8f7996f3c0480743c40ff00","user":{"login":"DIRACGrid","id":694842,"avatar_url":"https://avatars.githubusercontent.com/u/694842?v=3","gravatar_id":"","url":"https://api.github.com/users/DIRACGrid","html_url":"https://github.com/DIRACGrid","followers_url":"https://api.github.com/users/DIRACGrid/followers","following_url":"https://api.github.com/users/DIRACGrid/following{/other_user}","gists_url":"https://api.github.com/users/DIRACGrid/gists{/gist_id}","starred_url":"https://api.github.com/users/DIRACGrid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DIRACGrid/subscriptions","organizations_url":"https://api.github.com/users/DIRACGrid/orgs","repos_url":"https://api.github.com/users/DIRACGrid/repos","events_url":"https://api.github.com/users/DIRACGrid/events{/privacy}","received_events_url":"https://api.github.com/users/DIRACGrid/received_events","type":"Organization","site_admin":false},"repo":{"id":1576831,"name":"DIRAC","full_name":"DIRACGrid/DIRAC","owner":{"login":"DIRACGrid","id":694842,"avatar_url":"https://avatars.githubusercontent.com/u/694842?v=3","gravatar_id":"","url":"https://api.github.com/users/DIRACGrid","html_url":"https://github.com/DIRACGrid","followers_url":"https://api.github.com/users/DIRACGrid/followers","following_url":"https://api.github.com/users/DIRACGrid/following{/other_user}","gists_url":"https://api.github.com/users/DIRACGrid/gists{/gist_id}","starred_url":"https://api.github.com/users/DIRACGrid/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/DIRACGrid/subscriptions","organizations_url":"https://api.github.com/users/DIRACGrid/orgs","repos_url":"https://api.github.com/users/DIRACGrid/repos","events_url":"https://api.github.com/users/DIRACGrid/events{/privacy}","received_events_url":"https://api.github.com/users/DIRACGrid/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/DIRACGrid/DIRAC","description":"DIRAC Grid","fork":false,"url":"https://api.github.com/repos/DIRACGrid/DIRAC","forks_url":"https://api.github.com/repos/DIRACGrid/DIRAC/forks","keys_url":"https://api.github.com/repos/DIRACGrid/DIRAC/keys{/key_id}","collaborators_url":"https://api.github.com/repos/DIRACGrid/DIRAC/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/DIRACGrid/DIRAC/teams","hooks_url":"https://api.github.com/repos/DIRACGrid/DIRAC/hooks","issue_events_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/events{/number}","events_url":"https://api.github.com/repos/DIRACGrid/DIRAC/events","assignees_url":"https://api.github.com/repos/DIRACGrid/DIRAC/assignees{/user}","branches_url":"https://api.github.com/repos/DIRACGrid/DIRAC/branches{/branch}","tags_url":"https://api.github.com/repos/DIRACGrid/DIRAC/tags","blobs_url":"https://api.github.com/repos/DIRACGrid/DIRAC/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/DIRACGrid/DIRAC/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/DIRACGrid/DIRAC/git/refs{/sha}","trees_url":"https://api.github.com/repos/DIRACGrid/DIRAC/git/trees{/sha}","statuses_url":"https://api.github.com/repos/DIRACGrid/DIRAC/statuses/{sha}","languages_url":"https://api.github.com/repos/DIRACGrid/DIRAC/languages","stargazers_url":"https://api.github.com/repos/DIRACGrid/DIRAC/stargazers","contributors_url":"https://api.github.com/repos/DIRACGrid/DIRAC/contributors","subscribers_url":"https://api.github.com/repos/DIRACGrid/DIRAC/subscribers","subscription_url":"https://api.github.com/repos/DIRACGrid/DIRAC/subscription","commits_url":"https://api.github.com/repos/DIRACGrid/DIRAC/commits{/sha}","git_commits_url":"https://api.github.com/repos/DIRACGrid/DIRAC/git/commits{/sha}","comments_url":"https://api.github.com/repos/DIRACGrid/DIRAC/comments{/number}","issue_comment_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/comments{/number}","contents_url":"https://api.github.com/repos/DIRACGrid/DIRAC/contents/{+path}","compare_url":"https://api.github.com/repos/DIRACGrid/DIRAC/compare/{base}...{head}","merges_url":"https://api.github.com/repos/DIRACGrid/DIRAC/merges","archive_url":"https://api.github.com/repos/DIRACGrid/DIRAC/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/DIRACGrid/DIRAC/downloads","issues_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues{/number}","pulls_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls{/number}","milestones_url":"https://api.github.com/repos/DIRACGrid/DIRAC/milestones{/number}","notifications_url":"https://api.github.com/repos/DIRACGrid/DIRAC/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/DIRACGrid/DIRAC/labels{/name}","releases_url":"https://api.github.com/repos/DIRACGrid/DIRAC/releases{/id}","created_at":"2011-04-06T11:47:53Z","updated_at":"2015-08-23T15:07:00Z","pushed_at":"2015-10-19T17:32:53Z","git_url":"git://github.com/DIRACGrid/DIRAC.git","ssh_url":"git@github.com:DIRACGrid/DIRAC.git","clone_url":"https://github.com/DIRACGrid/DIRAC.git","svn_url":"https://github.com/DIRACGrid/DIRAC","homepage":"http://diracgrid.org","size":66142,"stargazers_count":44,"watchers_count":44,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":86,"mirror_url":null,"open_issues_count":123,"forks":86,"open_issues":123,"watchers":44,"default_branch":"integration"}},"_links":{"self":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652"},"html":{"href":"https://github.com/DIRACGrid/DIRAC/pull/2652"},"issue":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2652"},"comments":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2652/comments"},"review_comments":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652/comments"},"review_comment":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2652/commits"},"statuses":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/statuses/54aaf53506824e23461a425a262a6f55e6dad738"}}}}