{"action":"created","comment":{"url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments/38101956","id":38101956,"diff_hunk":"@@ -0,0 +1,836 @@\n+\"\"\" Main class for doing consistency checks, between files in:\n+    - File Catalog\n+    - TransformationSystem\n+\n+    Should be extended to include the Storage (in DIRAC)\n+\"\"\"\n+\n+import os\n+import copy\n+import time\n+import sys\n+import types\n+import re\n+\n+import DIRAC\n+\n+from DIRAC                                         import S_OK, S_ERROR, gLogger\n+from DIRAC.DataManagementSystem.Client.DataManager import DataManager\n+from DIRAC.Resources.Storage.StorageElement        import StorageElement\n+from DIRAC.Resources.Catalog.FileCatalog           import FileCatalog\n+from DIRAC.Core.Utilities.List                     import breakListIntoChunks\n+from DIRAC.Interfaces.API.Dirac                    import Dirac\n+from DIRAC.Core.Utilities.List                     import sortList\n+\n+from DIRAC.TransformationSystem.Client.TransformationClient import TransformationClient\n+from DIRAC.Core.Utilities.Adler                             import compareAdler\n+\n+class ConsistencyChecks( object ):\n+  \"\"\" A class for handling some consistency check\n+  \"\"\"\n+  def __init__( self, interactive = True, transClient = None, dm = None, fc = None ):\n+    \"\"\" c'tor\n+\n+        One object for every production/BkQuery/directoriesList...\n+    \"\"\"\n+    self.interactive = interactive\n+    self.transClient = TransformationClient() if transClient is None else transClient\n+    self.dm = DataManager() if dm is None else dm\n+    self.fc = FileCatalog() if fc is None else fc\n+\n+    self.dirac = Dirac()\n+\n+    # Base elements from which to start the consistency checks\n+    self._prod = 0\n+    self._bkQuery = None\n+    self._fileType = []\n+    self._fileTypesExcluded = []\n+    self._lfns = []\n+    self.noLFC = False\n+    self.directories = []\n+\n+    # Accessory elements\n+    self.runsList = []\n+    self.runStatus = None\n+    self.fromProd = None\n+    self.transType = ''\n+    self.cachedReplicas = {}\n+\n+    self.prcdWithDesc = []\n+    self.prcdWithoutDesc = []\n+    self.prcdWithMultDesc = []\n+    self.nonPrcdWithDesc = []\n+    self.nonPrcdWithoutDesc = []\n+    self.nonPrcdWithMultDesc = []\n+    self.descForPrcdLFNs = []\n+    self.descForNonPrcdLFNs = []\n+    self.removedFiles = []\n+\n+    self.absentLFNsInFC = []\n+    self.existLFNsNoSE = {}\n+    self.existLFNsBadReplicas = {}\n+    self.existLFNsBadFiles = {}\n+\n+    self.commonAncestors = {}\n+    self.multipleDescendants = {}\n+    self.ancestors = {}\n+\n+  ################################################################################\n+\n+  def getReplicasPresence( self, lfns ):\n+    \"\"\" get the replicas using the standard DataManager.getReplicas()\n+    \"\"\"\n+    present = set()\n+    notPresent = set()\n+\n+    chunkSize = 1000\n+    printProgress = ( len( lfns ) > chunkSize )\n+    startTime = time.time()\n+    self.__write( \"Checking replicas for %d files%s\" %\n+                  ( len( lfns ), ( ' (chunks of %d)' % chunkSize ) if printProgress else '... ' ) )\n+    for chunk in breakListIntoChunks( lfns, chunkSize ):\n+      if printProgress:\n+        self.__write( '.' )\n+      for _ in range( 1, 10 ):\n+        res = self.dm.getReplicas( chunk )\n+        if res['OK']:\n+          present.update( res['Value']['Successful'] )\n+          self.cachedReplicas.update( res['Value']['Successful'] )\n+          notPresent.update( res['Value']['Failed'] )\n+          break\n+        else:\n+          gLogger.error( \"\\nError getting replicas from FC, retry\", res['Message'] )\n+          time.sleep( 0.1 )\n+    self.__write( ' (%.1f seconds)\\n' % ( time.time() - startTime ) )\n+\n+    gLogger.info( \"Found %d files with replicas and %d without\" % ( len( present ), len( notPresent ) ) )\n+    return list( present ), list( notPresent )\n+\n+  ################################################################################\n+\n+  def getReplicasPresenceFromDirectoryScan( self, lfns ):\n+    \"\"\" Get replicas scanning the directories. Might be faster.\n+    \"\"\"\n+\n+    dirs = {}\n+    present = []\n+    notPresent = []\n+    compare = True\n+\n+    for lfn in lfns:\n+      dirN = os.path.dirname( lfn )\n+      if lfn == dirN + '/':\n+        compare = False\n+      dirs.setdefault( dirN, [] ).append( lfn )\n+\n+    if compare:\n+      self.__write( \"Checking File Catalog for %d files from %d directories \" % ( len( lfns ), len( dirs ) ) )\n+    else:\n+      self.__write( \"Getting files from %d directories \" % len( dirs ) )\n+    startTime = time.time()\n+\n+    for dirN in sorted( dirs ):\n+      startTime1 = time.time()\n+      self.__write( '.' )\n+      lfnsFound = self._getFilesFromDirectoryScan( dirN )\n+      gLogger.verbose( \"Obtained %d files in %.1f seconds\" % ( len( lfnsFound ), time.time() - startTime1 ) )\n+      if compare:\n+        pr, notPr = self.__compareLFNLists( dirs[dirN], lfnsFound )\n+        notPresent += notPr\n+        present += pr\n+      else:\n+        present += lfnsFound\n+\n+    self.__write( ' (%.1f seconds)\\n' % ( time.time() - startTime ) )\n+    gLogger.info( \"Found %d files with replicas and %d without\" % ( len( present ), len( notPresent ) ) )\n+    return present, notPresent\n+\n+  ################################################################################\n+\n+  @staticmethod\n+  def __compareLFNLists( lfns, lfnsFound ):\n+    \"\"\" return files in both lists and files in lfns and not in lfnsFound\n+    \"\"\"\n+    present = []\n+    notPresent = lfns\n+    startTime = time.time()\n+    gLogger.verbose( \"Comparing list of %d LFNs with second list of %d\" % ( len( lfns ), len( lfnsFound ) ) )\n+    if lfnsFound:\n+      setLfns = set( lfns )\n+      setLfnsFound = set( lfnsFound )\n+      present = list( setLfns & setLfnsFound )\n+      notPresent = list( setLfns - setLfnsFound )\n+\n+    gLogger.verbose( \"End of comparison: %.1f seconds\" % ( time.time() - startTime ) )\n+    return present, notPresent\n+\n+  def _getFilesFromDirectoryScan( self, dirs ):\n+    \"\"\" calls dm.getFilesFromDirectory\n+    \"\"\"\n+\n+    level = gLogger.getLevel()\n+    gLogger.setLevel( 'FATAL' )\n+    res = self.dm.getFilesFromDirectory( dirs )\n+    gLogger.setLevel( level )\n+    if not res['OK']:\n+      if 'No such file or directory' not in res['Message']:\n+        gLogger.error( \"Error getting files from directories %s:\" % dirs, res['Message'] )\n+      return []\n+    if res['Value']:\n+      lfnsFound = res['Value']\n+    else:\n+      lfnsFound = []\n+\n+    return lfnsFound\n+\n+  ################################################################################\n+\n+  def _getTSFiles( self ):\n+    \"\"\" Helper function - get files from the TS\n+    \"\"\"\n+\n+    selectDict = { 'TransformationID': self.prod}\n+    if self._lfns:\n+      selectDict['LFN'] = self._lfns\n+    elif self.runStatus and self.fromProd:\n+      res = self.transClient.getTransformationRuns( {'TransformationID': self.fromProd, 'Status':self.runStatus} )\n+      if not res['OK']:\n+        gLogger.error( \"Failed to get runs for transformation %d\" % self.prod )\n+      else:\n+        if res['Value']:\n+          self.runsList.extend( [run['RunNumber'] for run in res['Value'] if run['RunNumber'] not in self.runsList] )\n+          gLogger.always( \"%d runs selected\" % len( res['Value'] ) )\n+        elif not self.runsList:\n+          gLogger.always( \"No runs selected, check completed\" )\n+          DIRAC.exit( 0 )\n+    if not self._lfns and self.runsList:\n+      selectDict['RunNumber'] = self.runsList\n+\n+    res = self.transClient.getTransformation( self.prod )\n+    if not res['OK']:\n+      gLogger.error( \"Failed to find transformation %s\" % self.prod )\n+      return [], [], []\n+    status = res['Value']['Status']\n+    if status not in ( 'Active', 'Stopped', 'Completed', 'Idle' ):\n+      gLogger.always( \"Transformation %s in status %s, will not check if files are processed\" % ( self.prod, status ) )\n+      processedLFNs = []\n+      nonProcessedLFNs = []\n+      nonProcessedStatuses = []\n+      if self._lfns:\n+        processedLFNs = self._lfns\n+    else:\n+      res = self.transClient.getTransformationFiles( selectDict )\n+      if not res['OK']:\n+        gLogger.error( \"Failed to get files for transformation %d\" % self.prod, res['Message'] )\n+        return [], [], []\n+      else:\n+        processedLFNs = [item['LFN'] for item in res['Value'] if item['Status'] == 'Processed']\n+        nonProcessedLFNs = [item['LFN'] for item in res['Value'] if item['Status'] != 'Processed']\n+        nonProcessedStatuses = list( set( [item['Status'] for item in res['Value'] if item['Status'] != 'Processed'] ) )\n+\n+    return processedLFNs, nonProcessedLFNs, nonProcessedStatuses\n+\n+  ################################################################################\n+\n+  def __write( self, text ):","path":"DataManagementSystem/Client/ConsistencyChecks.py","position":235,"original_position":235,"commit_id":"878faac91f9430b552ca9fa7e3f8dbdd6c56abac","original_commit_id":"878faac91f9430b552ca9fa7e3f8dbdd6c56abac","user":{"login":"chaen","id":3728211,"avatar_url":"https://avatars.githubusercontent.com/u/3728211?v=3","gravatar_id":"","url":"https://api.github.com/users/chaen","html_url":"https://github.com/chaen","followers_url":"https://api.github.com/users/chaen/followers","following_url":"https://api.github.com/users/chaen/following{/other_user}","gists_url":"https://api.github.com/users/chaen/gists{/gist_id}","starred_url":"https://api.github.com/users/chaen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chaen/subscriptions","organizations_url":"https://api.github.com/users/chaen/orgs","repos_url":"https://api.github.com/users/chaen/repos","events_url":"https://api.github.com/users/chaen/events{/privacy}","received_events_url":"https://api.github.com/users/chaen/received_events","type":"User","site_admin":false},"body":"There are quite a bunch of information that you print with this while they would be interesting in the log, like progress reports. Also, why not using gLogger ? ","created_at":"2015-08-27T14:34:48Z","updated_at":"2015-08-27T14:34:48Z","html_url":"https://github.com/DIRACGrid/DIRAC/pull/2557#discussion_r38101956","pull_request_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557","_links":{"self":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments/38101956"},"html":{"href":"https://github.com/DIRACGrid/DIRAC/pull/2557#discussion_r38101956"},"pull_request":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557"}}},"pull_request":{"url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557","id":43291175,"html_url":"https://github.com/DIRACGrid/DIRAC/pull/2557","diff_url":"https://github.com/DIRACGrid/DIRAC/pull/2557.diff","patch_url":"https://github.com/DIRACGrid/DIRAC/pull/2557.patch","issue_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2557","number":2557,"state":"open","locked":false,"title":"[v6r15] Added consistency checks for DIRAC","user":{"login":"marcelovilaca","id":8600949,"avatar_url":"https://avatars.githubusercontent.com/u/8600949?v=3","gravatar_id":"","url":"https://api.github.com/users/marcelovilaca","html_url":"https://github.com/marcelovilaca","followers_url":"https://api.github.com/users/marcelovilaca/followers","following_url":"https://api.github.com/users/marcelovilaca/following{/other_user}","gists_url":"https://api.github.com/users/marcelovilaca/gists{/gist_id}","starred_url":"https://api.github.com/users/marcelovilaca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcelovilaca/subscriptions","organizations_url":"https://api.github.com/users/marcelovilaca/orgs","repos_url":"https://api.github.com/users/marcelovilaca/repos","events_url":"https://api.github.com/users/marcelovilaca/events{/privacy}","received_events_url":"https://api.github.com/users/marcelovilaca/received_events","type":"User","site_admin":false},"body":"","created_at":"2015-08-25T14:18:55Z","updated_at":"2015-08-27T14:34:48Z","closed_at":null,"merged_at":null,"merge_commit_sha":"24c001adbc1426eab158b933a27130bd9394723b","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557/commits","review_comments_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557/comments","review_comment_url":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments{/number}","comments_url":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2557/comments","statuses_url":"https://api.github.com/repos/DIRACGrid/DIRAC/statuses/878faac91f9430b552ca9fa7e3f8dbdd6c56abac","head":{"label":"marcelovilaca:consistencyChecks","ref":"consistencyChecks","sha":"878faac91f9430b552ca9fa7e3f8dbdd6c56abac","user":{"login":"marcelovilaca","id":8600949,"avatar_url":"https://avatars.githubusercontent.com/u/8600949?v=3","gravatar_id":"","url":"https://api.github.com/users/marcelovilaca","html_url":"https://github.com/marcelovilaca","followers_url":"https://api.github.com/users/marcelovilaca/followers","following_url":"https://api.github.com/users/marcelovilaca/following{/other_user}","gists_url":"https://api.github.com/users/marcelovilaca/gists{/gist_id}","starred_url":"https://api.github.com/users/marcelovilaca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcelovilaca/subscriptions","organizations_url":"https://api.github.com/users/marcelovilaca/orgs","repos_url":"https://api.github.com/users/marcelovilaca/repos","events_url":"https://api.github.com/users/marcelovilaca/events{/privacy}","received_events_url":"https://api.github.com/users/marcelovilaca/received_events","type":"User","site_admin":false},"repo":{"id":38313280,"name":"DIRAC","full_name":"marcelovilaca/DIRAC","owner":{"login":"marcelovilaca","id":8600949,"avatar_url":"https://avatars.githubusercontent.com/u/8600949?v=3","gravatar_id":"","url":"https://api.github.com/users/marcelovilaca","html_url":"https://github.com/marcelovilaca","followers_url":"https://api.github.com/users/marcelovilaca/followers","following_url":"https://api.github.com/users/marcelovilaca/following{/other_user}","gists_url":"https://api.github.com/users/marcelovilaca/gists{/gist_id}","starred_url":"https://api.github.com/users/marcelovilaca/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/marcelovilaca/subscriptions","organizations_url":"https://api.github.com/users/marcelovilaca/orgs","repos_url":"https://api.github.com/users/marcelovilaca/repos","events_url":"https://api.github.com/users/marcelovilaca/events{/privacy}","received_events_url":"https://api.github.com/users/marcelovilaca/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/marcelovilaca/DIRAC","description":"DIRAC Grid","fork":true,"url":"https://api.github.com/repos/marcelovilaca/DIRAC","forks_url":"https://api.github.com/repos/marcelovilaca/DIRAC/forks","keys_url":"https://api.github.com/repos/marcelovilaca/DIRAC/keys{/key_id}","collaborators_url":"https://api.github.com/repos/marcelovilaca/DIRAC/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/marcelovilaca/DIRAC/teams","hooks_url":"https://api.github.com/repos/marcelovilaca/DIRAC/hooks","issue_events_url":"https://api.github.com/repos/marcelovilaca/DIRAC/issues/events{/number}","events_url":"https://api.github.com/repos/marcelovilaca/DIRAC/events","assignees_url":"https://api.github.com/repos/marcelovilaca/DIRAC/assignees{/user}","branches_url":"https://api.github.com/repos/marcelovilaca/DIRAC/branches{/branch}","tags_url":"https://api.github.com/repos/marcelovilaca/DIRAC/tags","blobs_url":"https://api.github.com/repos/marcelovilaca/DIRAC/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/marcelovilaca/DIRAC/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/marcelovilaca/DIRAC/git/refs{/sha}","trees_url":"https://api.github.com/repos/marcelovilaca/DIRAC/git/trees{/sha}","statuses_url":"https://api.github.com/repos/marcelovilaca/DIRAC/statuses/{sha}","languages_url":"https://api.github.com/repos/marcelovilaca/DIRAC/languages","stargazers_url":"https://api.github.com/repos/marcelovilaca/DIRAC/stargazers","contributors_url":"https://api.github.com/repos/marcelovilaca/DIRAC/contributors","subscribers_url":"https://api.github.com/repos/marcelovilaca/DIRAC/subscribers","subscription_url":"https://api.github.com/repos/marcelovilaca/DIRAC/subscription","commits_url":"https://api.github.com/repos/marcelovilaca/DIRAC/commits{/sha}","git_commits_url":"https://api.github.com/repos/marcelovilaca/DIRAC/git/commits{/sha}","comments_url":"https://api.github.com/repos/marcelovilaca/DIRAC/comments{/number}","issue_comment_url":"https://api.github.com/repos/marcelovilaca/DIRAC/issues/comments{/number}","contents_url":"https://api.github.com/repos/marcelovilaca/DIRAC/contents/{+path}","compare_url":"https://api.github.com/repos/marcelovilaca/DIRAC/compare/{base}...{head}","merges_url":"https://api.github.com/repos/marcelovilaca/DIRAC/merges","archive_url":"https://api.github.com/repos/marcelovilaca/DIRAC/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/marcelovilaca/DIRAC/downloads","issues_url":"https://api.github.com/repos/marcelovilaca/DIRAC/issues{/number}","pulls_url":"https://api.github.com/repos/marcelovilaca/DIRAC/pulls{/number}","milestones_url":"https://api.github.com/repos/marcelovilaca/DIRAC/milestones{/number}","notifications_url":"https://api.github.com/repos/marcelovilaca/DIRAC/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/marcelovilaca/DIRAC/labels{/name}","releases_url":"https://api.github.com/repos/marcelovilaca/DIRAC/releases{/id}","created_at":"2015-06-30T14:16:41Z","updated_at":"2015-06-30T14:16:45Z","pushed_at":"2015-08-27T07:57:18Z","git_url":"git://github.com/marcelovilaca/DIRAC.git","ssh_url":"git@github.com:marcelovilaca/DIRAC.git","clone_url":"https://github.com/marcelovilaca/DIRAC.git","svn_url":"https://github.com/marcelovilaca/DIRAC","homepage":"http://diracgrid.org","size":27406,"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-v6r15","ref":"rel-v6r15","sha":"d5c84c406735fcb32fce6ac5ffa70297f54957a7","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-08-27T13:46:03Z","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":64022,"stargazers_count":44,"watchers_count":44,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":82,"mirror_url":null,"open_issues_count":113,"forks":82,"open_issues":113,"watchers":44,"default_branch":"integration"}},"_links":{"self":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557"},"html":{"href":"https://github.com/DIRACGrid/DIRAC/pull/2557"},"issue":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2557"},"comments":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/issues/2557/comments"},"review_comments":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557/comments"},"review_comment":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/pulls/2557/commits"},"statuses":{"href":"https://api.github.com/repos/DIRACGrid/DIRAC/statuses/878faac91f9430b552ca9fa7e3f8dbdd6c56abac"}}}}