Skip to content

Commit

Permalink
Merge pull request #59 from gamcil/allowlocalhmm
Browse files Browse the repository at this point in the history
Allow local profile HMMs
  • Loading branch information
gamcil authored Jul 27, 2021
2 parents c638dd7 + 673e0f6 commit 454b3bd
Show file tree
Hide file tree
Showing 12 changed files with 2,818 additions and 46 deletions.
2 changes: 1 addition & 1 deletion cblaster/gui/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def run_cblaster(values, textbox):
args = dict(
query_file=values["query_file"],
query_ids=values["query_ids"],
query_profiles=values["query_profiles"],
query_profiles=values["query_profiles"].split(";") + values["query_pfams"].split(" "),
session_file=values["session_file"],
mode=values["search_mode"],
gap=values["gap"],
Expand Down
2 changes: 1 addition & 1 deletion cblaster/gui/parts.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import PySimpleGUI as sg


TEXT_WIDTH = 65
TEXT_WIDTH = 70


def SectionLabel(text):
Expand Down
15 changes: 11 additions & 4 deletions cblaster/gui/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@
"should refer to files generated using cblaster makedb. When using any "
"of the HMM modes, a local copy of the Pfam database will be stored at "
"the indicated location or extracted from there. The FASTA database "
"should refer to the FASTA file generated using cblaster makedb.",
size=(TEXT_WIDTH, 6))],
"should refer to the FASTA file generated using cblaster makedb.\n"
"NOTE: you must set an e-mail using cblaster config before doing "
"a search with cblaster.",
size=(TEXT_WIDTH, 8))],
[search_tabgroup]
]
)
Expand All @@ -141,7 +143,12 @@
sg.InputText(size=(34, 1), key="query_file"),
sg.FileBrowse(key="query_file")],
[TextLabel("Sequence IDs"), sg.InputText(key="query_ids", default_text="e.g. space separated values")],
[TextLabel("HMM profiles"), sg.InputText(key="query_profiles", default_text="e.g. space separated values")],
[TextLabel("Pfam domains"),
sg.InputText(key="query_pfams",
default_text="e.g. space separated values")],
[TextLabel("Local profile HMMs"),
sg.Input(size=(34, 1), key="query_profiles"),
sg.FilesBrowse()],
[TextLabel("Session file"),
sg.InputText(size=(34, 1), key="session_file"),
sg.FileBrowse(key="session_file")],
Expand Down Expand Up @@ -309,7 +316,7 @@
" at which point the figure can be manipulated and saved as SVG."
" If a file path is specified, a static HTML file will be generated at"
" that path.",
size=(TEXT_WIDTH, 4)
size=(TEXT_WIDTH, 5)
)],
[TextLabel("Output file"),
sg.InputText(key="figure_text", disabled=True, size=(34, 1)),
Expand Down
153 changes: 115 additions & 38 deletions cblaster/hmm_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import gzip
import subprocess
import logging
import re

from datetime import datetime
from ftplib import FTP
from pathlib import Path
from typing import Union, List, Collection, Set, Tuple

from Bio import SearchIO
from cblaster.classes import Hit
from cblaster.classes import Hit, Session
from cblaster import helpers

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -49,27 +51,54 @@ def check_pfam_db(path):
return hmm, dat


def get_full_accession_number(dat_path, keys):
def get_pfam_accession(
dat_path: Union[str, Path],
keys: Collection[str]
) -> Tuple[Set[str], Set[str]]:
"""Get full accession number of Pfam profiles
Looks for keys in ID and AC attributes, such that accessions
can be retrieved by name or accession.
Args:
keys: List, Strings of accession profiles numbers
db_path: String, Path to dat.gz file with the full acc-nr
keys: Strings of accession profiles numbers
db_path: Path to dat.gz file with the full acc-nr
Return:
key_lines: List, string of full acc-number
"""
valid_keys = []
keys = set(keys)
valid_keys = set()
name_attrs = ("#=GF ID", "#=GF AC")
for line in gzip.open(dat_path, "rt"):
if not line.startswith("#=GF AC"):
if not line.startswith(name_attrs):
continue
*_, accession = line.strip().split(" ")
for key in keys:
if key in accession:
valid_keys.append(accession)
return valid_keys
if any(key in accession for key in keys if key not in valid_keys):
valid_keys.add(accession)
return valid_keys, keys.difference(valid_keys)


def read_profiles(files: Collection[str]) -> Collection[str]:
"""Reads in profile HMMs from a list of files."""
profiles = []
for file in files:
with open(file) as fp:
profile = fp.read()
profiles.append(profile)
return profiles


def get_profile_names(profiles: Collection[str]) -> Collection[str]:
"""Extracts names from profile HMMs using regular expressions."""
names = []
pattern = re.compile(r"^NAME\s+?(?P<name>\w.+?)$", re.M)
for profile in profiles:
matches = [match.group("name") for match in pattern.finditer(profile)]
names.extend(matches)
return names


def fetch_profiles(hmm, dat, keys):
def fetch_pfam_profiles(hmm, keys):
"""Fetch hmm profiles from db and save in a file
Args:
Expand All @@ -78,26 +107,38 @@ def fetch_profiles(hmm, dat, keys):
Return:
ls_keys: List, strings with acc-numbers
"""
LOG.info("Fetching profiles from Pfam-A file")
if not isinstance(hmm, Path):
hmm = Path(hmm)

LOG.info("Profiles found: %s", keys)
profiles = []
for key in keys:
result = subprocess.run(["hmmfetch", hmm, key], stdout=subprocess.PIPE)
result = subprocess.run(
["hmmfetch", hmm, key],
stdout=subprocess.PIPE,
encoding="utf-8",
)
profiles.append(result.stdout)
return profiles


def write_profiles(profiles: Collection[str], output: str=None) -> str:
"""Writes a collection of profile HMMs to disk.
output = hmm.parent / datetime.now().strftime("cblaster_%Y%m%d%H%M%S.hmm")
with output.open("wb") as fp:
If no output file is specified, will randomly generate a file name and save
in the current working directory.
Args:
profiles: profile HMMs to write
output: name of output file
"""
if not output:
output = datetime.now().strftime("cblaster_%Y%m%d%H%M%S.hmm")
with open(output, "w") as fp:
for profile in profiles:
fp.write(profile)

LOG.info("Saved profiles to %s", output)
return output


def run_hmmsearch(pfam, fasta, query):
def run_hmmsearch(fasta, query):
"""Run the hmmsearch command
Args:
Expand All @@ -108,10 +149,10 @@ def run_hmmsearch(pfam, fasta, query):
temp_res: List, String of result file names
"""
LOG.info("Performing hmmsearch")
output = query.with_suffix(".txt")
output = Path(query).with_suffix(".txt")
try:
subprocess.run(
f"hmmsearch --cut_tc -o {output} {query} {fasta}",
f"hmmsearch -o {output} {query} {fasta}",
stdout=subprocess.PIPE,
shell=True,
check=True,
Expand All @@ -136,7 +177,7 @@ def parse_hmmer_output(results):
continue
for hit in record.hits:
hit_class = Hit(
query=record.accession, # Pfam id
query=record.id, # Pfam id
subject=hit.id, # Hit id
identity=None, # Not present
coverage=None, # Not present
Expand All @@ -149,37 +190,73 @@ def parse_hmmer_output(results):
return hit_info


def perform_hmmer(fasta, query_profiles, pfam, session):
def group_profiles(profiles: Collection[str]) -> tuple:
"""Group input query profile HMMs by Pfam, custom or invalid.
If the profile is found on disk, it will be loaded directly.
If not found locally, but starts with PF, will try to extract from Pfam.
Otherwise, marked as invalid.
"""
local = []
other = []
for profile in profiles:
if Path(profile).exists():
local.append(profile)
else:
other.append(profile)
return local, other


def perform_hmmer(
fasta: str,
query_profiles: List[str],
pfam: str,
session: Session,
) -> Union[Collection[Hit], None]:
"""Main of running a hmmer search
Args:
database_pfam: String, Path to pfam db
database: String, Path to seqeunce db, in fasta or gbk format
query_profiles: List, Pfam profiles needed to be searched
fasta: Path to database FASTA file
query_profiles: Pfam names/accessions, or paths to profile HMM files
pfam: Path to folder containing Pfam database
session: cblaster search session
Returns:
hit_res: List of class objects with the hits
List of class objects with the hits
"""
LOG.info("Starting hmmer search")

# Make sure we can find hmmfetch and hmmsearch on PATH
helpers.get_program_path(["hmmfetch", "hmmsearch"])

# Find Pfam database (.dat and .hmm)
hmm, dat = check_pfam_db(pfam)
# Divide profiles into Pfam, local files and invalids
local_profiles, other_profiles = group_profiles(query_profiles)

# Find real Pfam accessions
session.queries = get_full_accession_number(dat, query_profiles)

if not session.queries:
# Read in/fetch all query profile HMMs.
# Local profiles are just read in straight from file, Pfam accessions are
# extracted from a local copy of the Pfam database (require .dat and .hmm).
profiles = []
if local_profiles:
LOG.info("Loading local profiles: %s", local_profiles)
profiles = read_profiles(local_profiles)
if other_profiles:
LOG.info("Fetching accessions from Pfam: %s", other_profiles)
hmm, dat = check_pfam_db(pfam)
accessions, invalid = get_pfam_accession(dat, other_profiles)
if invalid:
LOG.warning("Failed to fetch profiles from Pfam: %s", invalid)
pfam_profiles = fetch_pfam_profiles(hmm, accessions)
profiles.extend(pfam_profiles)
if not profiles:
LOG.error("No valid profiles could be selected")
return
query = write_profiles(profiles)
LOG.info("Profiles written to: %s", query)

# Extract HMM profiles from database
query = fetch_profiles(hmm, dat, session.queries)
# Save query profile HMM names
session.queries = get_profile_names(profiles)

# Run search
results = run_hmmsearch(hmm, fasta, query)
results = run_hmmsearch(fasta, query)

# Parse results and return
return parse_hmmer_output(results)
4 changes: 2 additions & 2 deletions cblaster/plot/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ body {
margin: 0;
padding: 0;
overflow: hidden;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Ubuntu, "Helvetica Neue", Oxygen, Cantarell, sans-serif;
}

.tooltip {
Expand Down Expand Up @@ -96,6 +97,7 @@ body {
max-height: 95vh;
border: 1px solid black;
background-color: white;
font-size: 0.9rem;
}

#div-summary {
Expand All @@ -108,8 +110,6 @@ body {
#div-summary p {
margin: 0;
margin-bottom: 4px;
font-size: 10pt;
font-family: sans-serif;
}

#div-summary pre {
Expand Down
28 changes: 28 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
## Example cblaster search

In this folder you will find cblaster output generated from searching
the burnettramic acids gene cluster from *Aspergillus burnettii*, *bua*,
remotely against the NCBI's NR database.

| File | Description |
| ---- | ----------- |
| ``example/bua.gbk`` | *bua* cluster GenBank file |
| ``example/bua.fasta`` | FASTA file containing proteins in *bua* |
| ``example/bua_session.json`` | cblaster session file |
| ``example/bua_binary.txt`` | Binary absence/presence table output |
| ``example/bua_summary.txt`` | Default results summary |
| ``example/bua_results.html`` | Interactive output visualisation |

These files were generated with the following command:

cblaster search \
--query_file bua.gbk \
--session bua_session.json \
--binary bua_binary.txt \
--output bua_summary.txt \
--plot bua_results.html

Searching with the GenBank file allows for synteny scoring of detected clusters.
However, you could also search with the FASTA file by specifying:

--query_file bua.fasta
18 changes: 18 additions & 0 deletions example/bua.fasta
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
>QBE85641.1
MPGQKPVVLFLTNSEFGQSSVVLAVAQELVRQSACEVHVASFSALKQQVEALNVAFHALPGRSMKEALCDSGLPFLPKHAPGTRGAVESYRKGLQHVVAPWEPAGYEPIYRACLDLLDKLNPDLVAVDPLFGPGQDACNVRQCRYLVLSPASFKDHLVQVQPHLRVLWKYPVISSGLPCPLPLFLIIINFYLIFKLVLHTFLSKRVQTLTKWRNHIGIPGDLTTIYANFNEKVPWLLPSIPQSDFPLKIPTNVTGCGPIIPPFESVAHNPTASWLAQRPTILFNLGSHMKYKLTDAQQVVTALSMVLHTYPDMQILWKCELSHDDPSSKEPESSDSSAIQDLIPAELTDRIRVTSWITPSPMSILAHDSTIMYVHHGGSNSFHEALAAGVAQVVCPVWLDTYDVAARVEFLRVGLRGNGKAAPHLEGSELGAAICRTAHRLRSGDMGTRTRELQAHILAQEEGVSGTGPADVYGVGRRKAAKVILDMITA
>QBE85642.1
MGIQHMLPSTQTAIIAGPQGEFQLSHDVPVTPLADDEIIMKTAAVALNPVDTKLVGDFITPGAIFGFDCAGVIVAVGPKVGNGLAIGDRVCGSARGMNREKPLGGAFAEYVMLPADLTLRIPPAMTFAEAASLGTALVSACMSLFWTMQIPASLQEPAEKPFPVLVYGGSTATGTMLLQVLKICGVRTLTTCSPKNFDLVRSYGADEVFDYNSPTCAQDIREATRNNLKYAVDCITEDSTIKICYSAIGRAGGQYIALNPYPEHLATRKVIKPGWILATLITGEGSAWPEPYHREPDPEIRALAKPAYSAVQKLLDEGRLRSHPIRVKDGGLAAVLDGVEVLRKGEISGQKLVYCFN
>QBE85643.1
MLVPVLTLLGTLTATGTLVYHFDERLPVVRFRLLTTFLVLSSGQLLLYALWKVFLKPLCFSPFKHLPKPPLDQWPLWRDHEDGQRGGRAQVGIIHCRGILNGERLIVSSPTALAKIASDNYTFIKPMAIKLLAGRVLGMGLVLTERDEHKQQRKLFLPPFAPKHIRDLYPTFWRKSREVTERMGDEIHATGAGNGVFEIGEWAARVALDIITLSTMGKDFGSVRDADAPLAKVYHTVLQPTLGHVVIAVLKNFLPARLVEALPLRSNRHQGDAYDTIRGVCRDLLREKKDQLAGHHLGGKDILSVCLRYEDIAGVDEEEVINQMTTILGAGHETISVGITWAIYMLCLHRDWQARLREEVRATLPSPDRAQESASSADVERMPLMRAFLEEVLRWYPPIPMTMREPLVDTELDGQYVPRGTRIVVPIKAINREERYWGPDAKRFSPSRWLKNDREFNPSGGVSSKYGYLSFMHGPRSCVASEFARAEMACVVSAWVGRFDLDLSDEHFRDEENMRTSNGNFSGKPLEGLYVRAQVLEGW
>QBE85644.1
MTTPVKGTSPGPDEWHHLEDLNQRRRIQNRLSQRNRRTNLRNQVAQGQKQETAKHSEPKQNRQLFWVPPNSSTAKGKLVTTKGNKLESPRPPDPVTPPGRWPEIWHASLHEKPSGGALHGDLGDDMDSCWINTLIRSPVLPIDDDMLSCTPPIPTFDTTMTLDAAPVPTAMPVRDGGLSEGNTTHIQVPSANGVELGRTGGCKAPQDMHFTVQPQQPMTSIHTDKDRRDHFFGYAALHVAASRGHLAVVQLLTERGIPVDQLSSENETALHLAAEQGHCAVAHYLLERGADPHRGNQYGETALHVAAGHGHCDIVRLLFSYLPDLNIVDRNGHTALLRAVAEGHVDVVRVLLERGMDAGMKVGWPLDQKSI
>QBE85645.1
MEVERLDYTDFLNEAPGRQEAFVQHLYAALSRVGFAKITNHPIPESVILQLFAWTKGFFTLPLEHKRKAAHPPQPNPHRGWSCIGQEKLSVIAQGKAVLDLKESFDMGPGDDELYPNIWTDEGDLPGFRAFMEDFYGRCQSLHLQLLSAIARSMQLPDSYFAPLCSQNSSELRLNHYPAVSRHDLTTGTMRISSHTDFGTITLLFQDSVGGLEVEDQTRPGHYMPVAADDCTDIIVNVGDCLQRWTNDRLRSANHRVTLPRGLTHGMVDDRYSIAYFGKPSRDVSVRTLSALLRANEEAKYREEMTAWQYNQSRLLQTY
>QBE85646.1
MAIASSIGLVAEAIHRHKTPERPIESENDIARYPAEDEQWALDELQDELCQEEPSDSEDQPKKKIRNPAKLADDFLKRYPPPPSGSAPAGRLPLPVIIPQRRPGVRVRGFVRAYAPDLQACGIDQDTFMDFLVTMTRAGRAPQWMGAANLTAAAAFALPGHAIGCGVGFAIQVVNAIAMEMRGRVQANGFLQKLNQGFFQPRGLYCLVLSFDNTHEEAMTDESLATAIATTTDPKTGVRKYTDKLRSHSGTTGPSEFPESAPLVFPVLDWLETNANAEQAEKLGRYKKFRKFVADYYDRRAQAEYAARNPTSPLAAPPRRGFTSKLADPNDDTNKSPISLATGGLVPYNTTWRETRNSEGRRPPRKIADKVLYMIIVNMPSDDDMSRAESIMATEATTEPSVQSDDAEAAKG
>QBE85647.1
MFLLILYAFLTAVVARKLSAAVSVYRYRKMHGLLLARPLNQKGDVIGFSLFRGMQKSAREGLSLQRHYENTQQHGPTISAVLLGKSFISTSDPENIKAVLATQFQDFNLGERNDAFAPFLGRGIFTEDGPEWERSRALIRPSLSKAQVAELPIIEQHVQNLLSRIPGDGSTVDLQQLFFNFTLDSATHSLLGQPVGFQLSPAGSEAERFSRAFDDAQAFLQVRAKLGPFRGLVRNKAFEVNCQLVHSAVDRYVSEALGRSARPRSEDGKPGSMRYDLLSELASTMTDRTQIRNELLNVLLAARDTTASLLSSVFFMLARHPRVWSRLQQEVVQLEGQRPTYDKLREMRYVRAVLNEALRLFPPVPTNIRCATCHTSLPRGGGVDGLQPVFVANGTIVHYSIWTMHRSTAIYGSDAEEYRPERWLQESDEAPLRPGWGFLPFSGGPRICLGQQKALTEAAYVVIRMLQTFATVQSRDQRPWREHMGLVLSSFHGVQVALRHVEG
>QBE85648.1
MAAPTEVTLRNLSGTWTLSSSLSDDPSPALELQDVSWLVRNVIANAPVTISITQTVDPTTGVTKIASTQSTLGRTVSDVRYLEWEVEDQQNHPLFGSLSMRSRWAKASDLGEGFLIGNADGEAELIEVLAHGTMAGWTSHQVWAFEEIDGHRYHTRRALVTKGDQQVQVRMVYDWQTEE
>QBE85649.1
MAPQNEPIAIVGSGCRFPGEASSPSKLWDLLREPRDVLSKIDRFSADGFYNKDGHHHGSSNVLHSYQLSEDTRSFDAQFFNIPASEAESMDPQQRFIMEVVYEALESAAIKIEELSGSPTAVYVGVMCNDYAHITYADLESVPKYAATGTALSILANRISYFFNWTGPSMTIDTACSSSLVALHHAVQTLRGGTSKVAVAAGTNLIFTPTNYIAESNVNMLSPTGRSRMWDANADGYARGEGVAAVVLKTLSQAVADGDRIECVIRETGLNQDGRTPGITMPSSVAQAELIRSTYARAGLDVTRESDRPQFFEAHGTGTKAGDPEEAKAIYKAFFGQEDTMDARDTLYVGSIKTIIGHTEGTAGLAGLLKASLAVQNKTLPPNMHFHTLNPDIEPYYGKLQILTSVKPWPALASGVPRRVSVNSFGFGGANAHAIVESYEPSNGAIKVQSSKETAIPFTFSGYSEKSLMSQLTSFLEYLDSHPEPRLRDSAWTLSRRSAFSTRTTVSGTSIERLQEKLQSKIDSKIKDGKALGIRSSSKNDKILGVFTGQGAQWPRMGLRLLQSSATARRIFDDLERSLAELPTEDRPSWSLVQELEREAEDSRVMEAEFSQVLCTAVQVMLVDLLHSVGVSFDIVVGHSSGEIGAAYSAGYLSARDAIRIAYYRGKFGKLACGRDGVAGGMLAAGTDMADAKDLCELDDFVGRLQLAASNSSSSVTLSGDAEAVAWAQFVLEDEKKFARLLKVDTAYHSYHMQPCAEPYIEAMKRAGIQALKPQSDCRWFSSVLEGQEVSAAMSASLANSYWRDNLLQPVMFSQALEQAVSNTSDIGLVLEVGPHAALRGPATLTINDKLGRDVPYFGLLSRNADDVESFSDGIGAVWASLARCVIDFTRVDALLADGPEDQPRLCDNVPGYSWDHQRTYWMESRSSAALRLRPAAHHELLGVRVDSLNREYRWRNFIKPSQLSWTRGHQVQSQLIFPGAGFSVMALEAAKALAPAEKIALVELTDMQVLRAMAFQDENTAVEVICSLSNVIEDPDQANLTANFTCDMCLSKESGFVMAACGAVRLQLGAASSQLLPERTACPVRMNDVNIEHFYSTLWSLGYNYTDMFRSITSLQRTTDAASGIIHTTTEPDYTTSLTLHPATLDVAFQGIFGAMGAPGDGRLWTVLVPTRIKRITINPAVCGGTSGLGVDLPFDASVSVSPIDGVAGDVDIFDSTGVNKAVQVEGLQVAPLVPVTQSDDREVFSDTIWNFQEPDAARDVPKWTLTDEEWEHARYVERACFYYLKQLHDTITAEERDRCEWHPRKMLDWATEVVGVVSRGEHPIIRQEWMNDTWEMLKGPLDELTAKYEDFESLTHVGKNLIPFVKGEFSLLELVRNGGLLEHIYRNTYAFCEYNNYLANLVKQLSHRFPRMDIFEIGAGTGSTTEAVLRGIGDHYSSYTYTDLSAGFFPNAQETFKEHDAKMIYKIYDAEKEPGKQGYTERTYDLVIASNVLHATHSLETTLTNARKLLKPGGYLVMLEVTDVNPLRPTFFFGCLPGWWVGENDGRPHHPLVTKERWGELFDRTGFSGLDTSTPSHDVFMAPFSVMLTQAVDRQMALIRQPLQEDNRTTIDHLLILGGTGFTSFMLIEDVKHQLKRYAKHVIVVETLEALEASHFHSRQMLLSLVELDAPVFSPFTPERFAALQMLTEKCRNVLWVVRGASGEQPYANMMNGVARCLVGEQPDMRFQFVDFDMADKVDAGFIVRSLLQLQISDAWHTFIEPYRPVWTLEREVRYIQGQAHIPRYSPSLRRNLQYNSWRRTIRETVDPSSKYVILTHANGYYDLEEDNSPRPDPMAEDDRMAINVSRSSTVAVEIDGIGHLYILTGECELSGQRVLAFSSHNASRVQVKKDWVVSIGILSSDEPALIQMVTNVCLGTMLLNQTPRNGSLLVYEPTVALARILTALTSAEEPGRVLFTTTNRAKLDSGVAFSFIHPSSPDSSIARWVPAGVAGFVDASGGRKEQNMAARFARHLSSQCRVISMEGFYSNSSHQWGNAGGNSLSALLQHSTGLFTQGYKQCKQNQVQELSLDDVIGASTEHKEIRILNWKSQSKALVKLSPVQDEITFKGDRTYLLVGLTGELGRSLCRWMVQRGARYVVLTSRKPDVEPAWLELMQSYGAHIEVMAMDATDRKSTYNTVRKVQQTLPPVAGVGNGAMVLNDGLFNVISHQDFNQTLRPKVDGTTYLNELFQSPDLDFFIVFSSLAYVTGNFGQTSYAAANAFMASLVEGRKRRGLPGSVMNLAGIFGLGYITRTDRSILERLGKLGYANISEYDFHQFFAEAVLSGVPGSGRCHEISSALRPIDPDGETNPPAWLDMPRLSYYRHTKHAFTESGDTKSLSVRSQLKEQTTMEDVQRVLTNGLILTLYKQLGLDPEDDAISPDTSLVELGIDSLVAVDMRVWFTKELDLDMPVLKLLGGATVAAMVEDTMERMSPDLIPNVAQKDVTVAADRPSAPSDGVPAVGRSTAVSTTEHNSEEQESHAMETQELDESTTSGGECSSTKESSSSEATPPPSSVMSEDLAKVEETASIDGPKYVRKVKMGYGSLQFFFLVKHLDDPTVLNMQFRLPLQGSIKIPDLKYAVKMLGQRHEALRTAFFVDAENDDEPTQGVLETSPLQLETMQVTDSKEARRVCEDVQKYVFNIESGETIRILLLSITPSSHWLVLSFHHISIDGFSFNILLDEINALYQGQHLPPVKTQFTDVMCKQRQDLQAGFRRSELAYWQQVLGKIPDPIPLFPVAKLSSRLPVTRYHFEEAPMASIDAATAEQIRKQCRALKATRFHFFMTVLRIFLFAFLDTDELCIGFADASRADSGVSRTVGYLVNMLSLKFQRKPSQTFAQKVEEARKQSYAALANSTVPFNALLEKLEPPRSAAYTPVFQVFMDYLQHKFTAPKGLGVVEEQVYAHLTHNFFDLAVDINDVSASEILVRFRMQQYLYSASSVSLLLKSYVQLVKMCAYMEPNKAIGEPVPYDAQDIERAISLGQGPVVSSQWPSTPIERILDVAQARPEAPALVDGEGARLSYMEMIDRAHSIAGCLLTAGVAEGSTVGVYQEPTADSICSLLAIWIIGAVYLPLDRRVPCSRLSSIVQDCQPSAILCHERTLSDTPYLEATKQTAIITVPVNVAGIEAAPVPLNTGNGDQTSIILYTSGSTGVPKGLPIRHVSLLNQIEAMTTTFGVGAEMVLQQSAPSFDVSMQQILMALCNGGALYVVPNETRLDPVTITRLIASEKITWVHATPSEFTQWLRHGSAHLRAAKDWKFAFSSGEALSSDLVKGFEALRRPDVKLINVYGPAEAGVITGTEIDTTHVSAEPRSPISLGSPLANIAVYVVDRNLRPVPVGVSGEIVVAGAGNISGYLNRFELTAKLFLPDTITPRGYYPGQLATLYRSGDIGRYSPDGQLYYEGRIAGDTQVKLNGIRIDLKDVESAILETSGGEIVNAIVTDRRSPDFLVAHVELKADFPEAERKNFLTYIQQCLPLPKYMCPAMFIPLDHVPLNSHGKLDRRAIAARPLPTVDGDDQQGDADLSETELALRELWTGCLPEDVVKLTSISATTDFFHLGGNSYLLVRLQRLIRDRFNVSVPVMALYDASTLMAMALKIRNSQSLAVIDWDTETSVAQSLGASPCAEQPTAPRKTTDLTVVLTGATGYLGSRIMKALIASEQVSQIHCVAVRGHSAGVPRELAHSSDKLILHGGHLEDPLLGMSEEEFTFVARETDLVIHSGANRSFWDHYERLRGPNVLSTKTLVDLALQNQAPLHFISSGGVHLLCSGEDYAAESLASYLPPTDGSNGYIASKWASEVYLEKAAQKTSLPVYVHRLTPAPDVTPDAPMELLEEMSALAVKLQALPAPSGWTGTFDLTPAEALATGIAAAAVGAQAPMLESHQSARFIHHPSQVKMTMDHVAKYLDMLPSAEGFERLPPLQWAGRAKREGLTWHFSSTDFITMGG
Loading

0 comments on commit 454b3bd

Please sign in to comment.