nodes: 0, module; 1, function_definition; 2, function_name:best_structures; 3, parameters; 4, block; 5, identifier:uniprot_id; 6, default_parameter; 7, default_parameter; 8, default_parameter; 9, default_parameter; 10, expression_statement; 11, expression_statement; 12, if_statement; 13, comment:# if output dir is specified but not outname, use the uniprot; 14, if_statement; 15, if_statement; 16, comment:# Load a possibly existing json file; 17, if_statement; 18, expression_statement; 19, comment:# Filter for sequence identity percentage; 20, if_statement; 21, return_statement; 22, identifier:outname; 23, None; 24, identifier:outdir; 25, None; 26, identifier:seq_ident_cutoff; 27, float:0.0; 28, identifier:force_rerun; 29, False; 30, comment:"""Use the PDBe REST service to query for the best PDB structures for a UniProt ID.
  
      More information found here: https://www.ebi.ac.uk/pdbe/api/doc/sifts.html
      Link used to retrieve results: https://www.ebi.ac.uk/pdbe/api/mappings/best_structures/:accession
      The list of PDB structures mapping to a UniProt accession sorted by coverage of the protein and, if the same, resolution.
  
      Here is the ranking algorithm described by the PDB paper:
      https://nar.oxfordjournals.org/content/44/D1/D385.full
  
      "Finally, a single quality indicator is also calculated for each entry by taking the harmonic average
      of all the percentile scores representing model and model-data-fit quality measures and then subtracting
      10 times the numerical value of the resolution (in Angstrom) of the entry to ensure that resolution plays
      a role in characterising the quality of a structure. This single empirical 'quality measure' value is used
      by the PDBe query system to sort results and identify the 'best' structure in a given context. At present,
      entries determined by methods other than X-ray crystallography do not have similar data quality information
      available and are not considered as 'best structures'."
  
      Args:
          uniprot_id (str): UniProt Accession ID
          outname (str): Basename of the output file of JSON results
          outdir (str): Path to output directory of JSON results
          seq_ident_cutoff (float): Cutoff results based on percent coverage (in decimal form)
          force_rerun (bool): Obtain best structures mapping ignoring previously downloaded results
  
      Returns:
          list: Rank-ordered list of dictionaries representing chain-specific PDB entries. Keys are:
              * pdb_id: the PDB ID which maps to the UniProt ID
              * chain_id: the specific chain of the PDB which maps to the UniProt ID
              * coverage: the percent coverage of the entire UniProt sequence
              * resolution: the resolution of the structure
              * start: the structure residue number which maps to the start of the mapped sequence
              * end: the structure residue number which maps to the end of the mapped sequence
              * unp_start: the sequence residue number which maps to the structure start
              * unp_end: the sequence residue number which maps to the structure end
              * experimental_method: type of experiment used to determine structure
              * tax_id: taxonomic ID of the protein's original organism
  
      """; 31, assignment; 32, not_operator; 33, block; 34, boolean_operator; 35, block; 36, identifier:outname; 37, block; 38, not_operator; 39, block; 40, comment:# Otherwise run the web request; 41, else_clause; 42, assignment; 43, comparison_operator:seq_ident_cutoff != 0; 44, block; 45, identifier:data; 46, identifier:outfile; 47, string; 48, identifier:outdir; 49, expression_statement; 50, not_operator; 51, identifier:outdir; 52, expression_statement; 53, expression_statement; 54, expression_statement; 55, call; 56, with_statement; 57, expression_statement; 58, comment:# TODO: add a checker for a cached file of uniprot -> PDBs - can be generated within gempro pipeline and stored; 59, block; 60, identifier:data; 61, subscript; 62, identifier:seq_ident_cutoff; 63, integer:0; 64, for_statement; 65, assignment; 66, identifier:outname; 67, assignment; 68, assignment; 69, assignment; 70, attribute; 71, argument_list; 72, with_clause; 73, block; 74, call; 75, expression_statement; 76, if_statement; 77, comment:# Write the json file if specified; 78, if_statement; 79, call; 80, identifier:uniprot_id; 81, identifier:result; 82, identifier:data; 83, block; 84, identifier:outdir; 85, string; 86, identifier:outname; 87, identifier:uniprot_id; 88, identifier:outname; 89, call; 90, identifier:outfile; 91, call; 92, attribute; 93, identifier:force_rerun; 94, keyword_argument; 95, keyword_argument; 96, with_item; 97, expression_statement; 98, attribute; 99, argument_list; 100, assignment; 101, comparison_operator:response.status_code == 404; 102, block; 103, else_clause; 104, identifier:outfile; 105, block; 106, identifier:dict; 107, argument_list; 108, if_statement; 109, attribute; 110, argument_list; 111, attribute; 112, argument_list; 113, identifier:ssbio; 114, identifier:utils; 115, identifier:flag; 116, identifier:force_rerun; 117, identifier:outfile; 118, identifier:outfile; 119, as_pattern; 120, assignment; 121, identifier:log; 122, identifier:debug; 123, call; 124, identifier:response; 125, call; 126, attribute; 127, integer:404; 128, expression_statement; 129, expression_statement; 130, block; 131, with_statement; 132, expression_statement; 133, identifier:raw_data; 134, comparison_operator:result['coverage'] < seq_ident_cutoff; 135, block; 136, identifier:op; 137, identifier:join; 138, identifier:outdir; 139, identifier:outname; 140, string; 141, identifier:format; 142, identifier:outname; 143, call; 144, as_pattern_target; 145, identifier:raw_data; 146, call; 147, attribute; 148, argument_list; 149, attribute; 150, argument_list; 151, identifier:response; 152, identifier:status_code; 153, call; 154, assignment; 155, expression_statement; 156, expression_statement; 157, with_clause; 158, block; 159, call; 160, subscript; 161, identifier:seq_ident_cutoff; 162, expression_statement; 163, string_content:{}.json; 164, identifier:open; 165, argument_list; 166, identifier:f; 167, attribute; 168, argument_list; 169, string; 170, identifier:format; 171, identifier:uniprot_id; 172, identifier:requests; 173, identifier:get; 174, call; 175, keyword_argument; 176, attribute; 177, argument_list; 178, identifier:raw_data; 179, dictionary; 180, call; 181, assignment; 182, with_item; 183, expression_statement; 184, attribute; 185, argument_list; 186, identifier:result; 187, string; 188, call; 189, identifier:outfile; 190, string; 191, identifier:json; 192, identifier:load; 193, identifier:f; 194, string_content:{}: loaded existing json file; 195, attribute; 196, argument_list; 197, identifier:data; 198, dictionary; 199, identifier:log; 200, identifier:debug; 201, call; 202, pair; 203, attribute; 204, argument_list; 205, identifier:raw_data; 206, call; 207, as_pattern; 208, call; 209, identifier:log; 210, identifier:debug; 211, call; 212, string_content:coverage; 213, attribute; 214, argument_list; 215, string_content:r; 216, string; 217, identifier:format; 218, identifier:uniprot_id; 219, pair; 220, attribute; 221, argument_list; 222, identifier:uniprot_id; 223, dictionary; 224, identifier:log; 225, identifier:debug; 226, call; 227, attribute; 228, argument_list; 229, call; 230, as_pattern_target; 231, attribute; 232, argument_list; 233, attribute; 234, argument_list; 235, identifier:data; 236, identifier:remove; 237, identifier:result; 238, string_content:https://www.ebi.ac.uk/pdbe/api/mappings/best_structures/{}; 239, string; 240, string; 241, string; 242, identifier:format; 243, identifier:uniprot_id; 244, attribute; 245, argument_list; 246, identifier:response; 247, identifier:json; 248, identifier:open; 249, argument_list; 250, identifier:f; 251, identifier:json; 252, identifier:dump; 253, identifier:raw_data; 254, identifier:f; 255, string; 256, identifier:format; 257, identifier:uniprot_id; 258, string_content:key; 259, string_content:value; 260, string_content:{}: 404 returned, probably no structures available.; 261, string; 262, identifier:format; 263, identifier:uniprot_id; 264, identifier:outfile; 265, string; 266, string_content:{}: Saved json file of best structures; 267, string_content:{}: Obtained best structures; 268, string_content:w
code: def best_structures(uniprot_id, outname=None, outdir=None, seq_ident_cutoff=0.0, force_rerun=False):
      """Use the PDBe REST service to query for the best PDB structures for a UniProt ID.
  
      More information found here: https://www.ebi.ac.uk/pdbe/api/doc/sifts.html
      Link used to retrieve results: https://www.ebi.ac.uk/pdbe/api/mappings/best_structures/:accession
      The list of PDB structures mapping to a UniProt accession sorted by coverage of the protein and, if the same, resolution.
  
      Here is the ranking algorithm described by the PDB paper:
      https://nar.oxfordjournals.org/content/44/D1/D385.full
  
      "Finally, a single quality indicator is also calculated for each entry by taking the harmonic average
      of all the percentile scores representing model and model-data-fit quality measures and then subtracting
      10 times the numerical value of the resolution (in Angstrom) of the entry to ensure that resolution plays
      a role in characterising the quality of a structure. This single empirical 'quality measure' value is used
      by the PDBe query system to sort results and identify the 'best' structure in a given context. At present,
      entries determined by methods other than X-ray crystallography do not have similar data quality information
      available and are not considered as 'best structures'."
  
      Args:
          uniprot_id (str): UniProt Accession ID
          outname (str): Basename of the output file of JSON results
          outdir (str): Path to output directory of JSON results
          seq_ident_cutoff (float): Cutoff results based on percent coverage (in decimal form)
          force_rerun (bool): Obtain best structures mapping ignoring previously downloaded results
  
      Returns:
          list: Rank-ordered list of dictionaries representing chain-specific PDB entries. Keys are:
              * pdb_id: the PDB ID which maps to the UniProt ID
              * chain_id: the specific chain of the PDB which maps to the UniProt ID
              * coverage: the percent coverage of the entire UniProt sequence
              * resolution: the resolution of the structure
              * start: the structure residue number which maps to the start of the mapped sequence
              * end: the structure residue number which maps to the end of the mapped sequence
              * unp_start: the sequence residue number which maps to the structure start
              * unp_end: the sequence residue number which maps to the structure end
              * experimental_method: type of experiment used to determine structure
              * tax_id: taxonomic ID of the protein's original organism
  
      """
      outfile = ''
  
      if not outdir:
          outdir = ''
  
      # if output dir is specified but not outname, use the uniprot
      if not outname and outdir:
          outname = uniprot_id
  
      if outname:
          outname = op.join(outdir, outname)
          outfile = '{}.json'.format(outname)
  
      # Load a possibly existing json file
      if not ssbio.utils.force_rerun(flag=force_rerun, outfile=outfile):
          with open(outfile, 'r') as f:
              raw_data = json.load(f)
          log.debug('{}: loaded existing json file'.format(uniprot_id))
  
      # Otherwise run the web request
      else:
          # TODO: add a checker for a cached file of uniprot -> PDBs - can be generated within gempro pipeline and stored
          response = requests.get('https://www.ebi.ac.uk/pdbe/api/mappings/best_structures/{}'.format(uniprot_id),
                                  data={'key': 'value'})
          if response.status_code == 404:
              log.debug('{}: 404 returned, probably no structures available.'.format(uniprot_id))
              raw_data = {uniprot_id: {}}
          else:
              log.debug('{}: Obtained best structures'.format(uniprot_id))
              raw_data = response.json()
  
          # Write the json file if specified
          if outfile:
              with open(outfile, 'w') as f:
                  json.dump(raw_data, f)
              log.debug('{}: Saved json file of best structures'.format(uniprot_id))
  
      data = dict(raw_data)[uniprot_id]
  
      # Filter for sequence identity percentage
      if seq_ident_cutoff != 0:
          for result in data:
              if result['coverage'] < seq_ident_cutoff:
                  data.remove(result)
  
      return data