diff --git a/INSTALL b/INSTALL index 870d2b4afb597306ae488812a2bb4f1e792757bb..511ed443201ad7ebdb8f8011bcddee06e58c31ec 100644 --- a/INSTALL +++ b/INSTALL @@ -144,6 +144,12 @@ directory: sudo bash extras/post-upgrade If you installed Mutalyzer in a development environment, you don't have to -do anything to upgrade except for running the automated migration scripts: +do anything usually, except for the following situations. + +* If the database has changed, run: for M in extras/migrations/*.migration; do sudo $M migrate; done + +* If the Mutalyzer version has changed, run: + + sudo python setup.py develop diff --git a/README b/README index 1390caec4c4de2fb6a20c477ef9d13f1e4155c70..a10d1b298c5f2e5a61f3ab895ac8923a09a0f6cf 100644 --- a/README +++ b/README @@ -34,6 +34,80 @@ Or, if you are in a hurry, skip the long-running tests with: MUTALYZER_ENV=test MUTALYZER_QUICK_TEST=1 nosetests -v +Release management +------------------ + +The current Mutalyzer version is recorded in `mutalyzer/__init__.py`. See the +comments in that file for more info of the versioning scheme. + +On the event of a new release, the following is done: + + emacs mutalyzer/__init__.py + +Remove `'dev'` from `__version_info__` and set `RELEASE` to `True`. + + svn commit -m 'Release 2.0.beta-11' + svn cp https://humgenprojects.lumc.nl/svn/mutalyzer/trunk \ + https://humgenprojects.lumc.nl/svn/mutalyzer/tags/mutalyzer-2.0.beta-11 \ + -m 'Tag 2.0.beta-11' + emacs mutalyzer/__init__.py + +Set `__version_info__` to a new version ending with `'dev'` and set `RELEASE` +to `FALSE`. + + svn commit -m 'Open development for 2.0.beta-12' + +Be sure to upgrade your installations to the new version as described in the +INSTALL file (e.g. `sudo python setup.py develop` for development checkouts). + + +Working with feature branches +----------------------------- + +Some features are best developed isolated in a separate branch (a 'feature +branch') before being merged into trunk. To make it easier to switch between +branches, don't checkout the entire repository tree from its root. Instead, +checkout the branch subdirectory (or trunk) directly and use `svn switch` to +switch between them. Otherwise, you might need to update you Apache config and +run `sudo python setup.py develop` every time you switch branches. + +To create the branch from current trunk: + + svn cp https://humgenprojects.lumc.nl/svn/mutalyzer/trunk \ + https://humgenprojects.lumc.nl/svn/mutalyzer/branches/my-feature-branch \ + -m 'Create my-feature-branch from trunk' + +To work from this branch, switch your checkout to there: + + svn switch https://humgenprojects.lumc.nl/svn/mutalyzer/branches/my-feature-branch + +To make it easier to reintegrate long-running branches, you can periodically +merge trunk into them: + + svn merge https://humgenprojects.lumc.nl/svn/mutalyzer/trunk + +Use `mergeinfo` to see which revisions are already merged. You can cherry-pick +revisions to merge by using the `-c` option: + + svn mergeinfo https://humgenprojects.lumc.nl/svn/mutalyzer/trunk \ + --show-revs eligible + svn merge -c 354 https://humgenprojects.lumc.nl/svn/mutalyzer/trunk + +If you just want to mark a revision as being merged, you can use the +`--record-only` switch. + +Reintegrating a branch into trunk is done from the trunk checkout: + + svn switch https://humgenprojects.lumc.nl/svn/mutalyzer/trunk + svn merge --reintegrate --dry-run \ + https://humgenprojects.lumc.nl/svn/mutalyzer/branches/my-feature-branch + svn rm https://humgenprojects.lumc.nl/svn/mutalyzer/branches/my-feature-branch \ + -m 'Delete refactor-mutalyzer-branch' + +All `merge` operations taken an optional `--dry-run` switch. Use it to see +what would happen without having to actually applying. + + Development notes ----------------- @@ -77,6 +151,8 @@ Todo list: Good read: http://news.ycombinator.com/item?id=3002861 Suggestion: http://celeryproject.org/ - Have a normal 404 page. +- Maintenance (and/or read-only) mode. +- Cleanup this document. Code style guide: - Follow PEP 8 (code) and PEP 257 (docstrings). diff --git a/extras/cron.d/mutalyzer-cache-sync b/extras/cron.d/mutalyzer-cache-sync index c58ea7742d54a461e7a93ee102cd6c9a3274a492..02d3e0a349e261bdae898929d7629a768fdde4f5 100644 --- a/extras/cron.d/mutalyzer-cache-sync +++ b/extras/cron.d/mutalyzer-cache-sync @@ -1,2 +1,2 @@ # Synchronize the local cache with the live server every morning at 05:25 -#25 5 * * * www-data <MUTALYZER_BIN_CACHE_SYNC> 'http://www.mutalyzer.nl/2.0/services/?wsdl' 'http://www.mutalyzer.nl/2.0/Reference/{file}' 3 +#25 5 * * * www-data <MUTALYZER_BIN_CACHE_SYNC> 'https://mutalyzer.nl/services/?wsdl' 'https://mutalyzer.nl/Reference/{file}' 3 diff --git a/extras/migrations/001-db-gbinfo-add-created.migration b/extras/migrations/001-db-gbinfo-add-created.migration index 26fd668a98012159486e81b7f6853f09cbf91e1e..686563ce39af39dddd5fe1e66afcab9e10a69721 100755 --- a/extras/migrations/001-db-gbinfo-add-created.migration +++ b/extras/migrations/001-db-gbinfo-add-created.migration @@ -36,6 +36,7 @@ def migrate(): ALTER TABLE GBInfo ADD COLUMN created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, ADD INDEX created (created);""") + cursor.execute('UPDATE GBInfo SET created = CURRENT_TIMESTAMP;') connection.commit() connection.close() migration.info('Added column mutalyzer.GBInfo.created') diff --git a/extras/post-install.sh b/extras/post-install.sh index d9dfa1ea798a7e7d8038d1530687275a7b06e909..9f7550db04398da04c0dfd3a8fb09788776edcd9 100644 --- a/extras/post-install.sh +++ b/extras/post-install.sh @@ -46,8 +46,9 @@ for USERNAME in $(cut -f 1 -d : /etc/passwd); do echo -e "${COLOR_INFO}Creating /home/${USERNAME}/.cache/mutalyzer${COLOR_END}" su $USERNAME -c "mkdir -p /home/$USERNAME/.config/mutalyzer" su $USERNAME -c "mkdir -p /home/$USERNAME/.cache/mutalyzer" - su $USERNAME -c "cp extras/config.user.example /home/$USERNAME/.config/mutalyzer/config" + su $USERNAME -c "touch /home/$USERNAME/.config/mutalyzer/config" su $USERNAME -c "touch /tmp/mutalyzer-$USERNAME.log" + cat extras/config.user.example > /home/$USERNAME/.config/mutalyzer/config sed -i -e "s@<USERNAME>@${USERNAME}@g" /home/$USERNAME/.config/mutalyzer/config fi done diff --git a/extras/pre-install.sh b/extras/pre-install.sh index 8af0b585beae93da581e28719483aa4324ff0f79..a49e65bc0ac8b1d293f0dd915fd84d67e508acf5 100644 --- a/extras/pre-install.sh +++ b/extras/pre-install.sh @@ -20,6 +20,7 @@ COLOR_END='\033[0m' echo -e "${COLOR_INFO}Installing packages with apt${COLOR_END}" +apt-get update && \ apt-get install -y \ mysql-server \ python \ diff --git a/mutalyzer/Crossmap.py b/mutalyzer/Crossmap.py index cd289e3ebd5e558886bc1204401f4e81cb759847..20fb8edc6147cb09c289a7754ab8dd2180b6ca1b 100644 --- a/mutalyzer/Crossmap.py +++ b/mutalyzer/Crossmap.py @@ -428,8 +428,7 @@ class Crossmap() : @return: The converted notation (may be unaltered) @rtype: integer """ - - if s[0] == '*' : + if s[0] == '*': return self.__STOP + int(s[1:]) return int(s) diff --git a/mutalyzer/Retriever.py b/mutalyzer/Retriever.py index d77f7fa63b00c933ff71d36a520e77822b32fced..ded70bcc034d797cbe7a16ff5f2e6b87c706f288 100644 --- a/mutalyzer/Retriever.py +++ b/mutalyzer/Retriever.py @@ -19,6 +19,7 @@ import StringIO # StringIO() from Bio import SeqIO # read() from Bio import Entrez # efetch(), read(), esearch(), esummary() from Bio.Seq import UnknownSeq +from Bio.Alphabet import ProteinAlphabet from xml.dom import DOMException, minidom from xml.parsers import expat @@ -734,7 +735,15 @@ class GenBankRetriever(Retriever): # Now we have the file, so we can parse it. GenBankParser = genbank.GBparser() - return GenBankParser.create_record(filename) + record = GenBankParser.create_record(filename) + + # Todo: This will change once we support protein references + if isinstance(record.seq.alphabet, ProteinAlphabet): + self._output.addMessage(__file__, 4, 'EPROTEINREF', + 'Protein reference sequences are not supported.') + return None + + return record #loadrecord #GenBankRetriever diff --git a/mutalyzer/mapping.py b/mutalyzer/mapping.py index 0cb27e7e41ce749205850e094546b0176889f5f6..afc5b837d5de2c95dbecdf76cda037da772549ac 100644 --- a/mutalyzer/mapping.py +++ b/mutalyzer/mapping.py @@ -254,15 +254,27 @@ class Converter(object) : @rtype: triple (integer, integer, integer) """ if Type == 'c' : - main = C.main2int(Loc.MainSgn + Loc.Main) - offset = C.offset2int(Loc.OffSgn + Loc.Offset) - g = C.x2g(main, offset) - main, offset = C.g2x(g) - #if - else : - g = int(Loc.Main) + if Loc.IVSLoc: + ivs_number = int(Loc.IVSLoc.IVSNumber) + if ivs_number < 1 or ivs_number > C.numberOfIntrons(): + # Todo: Error handling in this entire module is 'suboptimal' + raise Exception('Invalid intron') + if Loc.IVSLoc.OffSgn == '+': + g = C.getSpliceSite(ivs_number * 2 - 1) + \ + C.orientation * int(Loc.IVSLoc.Offset) + else: + g = C.getSpliceSite(ivs_number * 2) - \ + C.orientation * int(Loc.IVSLoc.Offset) + main, offset = C.g2x(g) + else: + main = C.main2int(Loc.PtLoc.MainSgn + Loc.PtLoc.Main) + offset = C.offset2int(Loc.PtLoc.OffSgn + Loc.PtLoc.Offset) + g = C.x2g(main, offset) + main, offset = C.g2x(g) + else: + g = int(Loc.PtLoc.Main) main, offset = C.g2x(g) - #else + return (main, offset, g) #_getcoords @@ -285,13 +297,13 @@ class Converter(object) : # Get the coordinates of the start position startmain, startoffset, start_g = \ - self._getcoords(Cross, mutation.StartLoc.PtLoc, + self._getcoords(Cross, mutation.StartLoc, self.parseTree.RefType) # If there is an end position, calculate the coordinates. if mutation.EndLoc : endmain, endoffset, end_g = \ - self._getcoords(Cross, mutation.EndLoc.PtLoc, + self._getcoords(Cross, mutation.EndLoc, self.parseTree.RefType) else : end_g, endmain, endoffset = start_g, startmain, startoffset diff --git a/mutalyzer/parsers/genbank.py b/mutalyzer/parsers/genbank.py index 247bcfbed4ba01f1e85b49b46822e95627702523..4dff2aba049b98bb0d0cc6f5ff0c151a3d4f9a68 100644 --- a/mutalyzer/parsers/genbank.py +++ b/mutalyzer/parsers/genbank.py @@ -6,6 +6,7 @@ mutalyzer GenRecord. Record populated with data from a GenBank file. import bz2 from Bio import SeqIO, Entrez +from Bio.Alphabet import ProteinAlphabet from mutalyzer.config import Config from mutalyzer import Db @@ -457,6 +458,10 @@ class GBparser(): # Todo: also set organism in the LRG parser record.organism = biorecord.annotations['organism'] + # Todo: This will change once we support protein references + if isinstance(biorecord.seq.alphabet, ProteinAlphabet): + return record + exonList = [] geneDict = {} diff --git a/tests/test_website.py b/tests/test_website.py index 61a7206ba4c2565311161a0f544d720c7e79cbcc..97a67cd5d36950d722d41e0fd0e28039df257316 100644 --- a/tests/test_website.py +++ b/tests/test_website.py @@ -175,6 +175,19 @@ class TestWSGI(): '0 Warnings', 'Details of the parse error') + def test_check_protein_reference(self): + """ + Submit the name checker form with a protein reference sequence (not + supported). + """ + r = self.app.get('/check') + form = r.forms[0] + form['mutationName'] = 'BAA81889.1:c.274G>T' + r = form.submit() + r.mustcontain('1 Error', + '0 Warnings', + 'Protein reference sequences are not supported') + def test_check_noninteractive(self): """ Submit the name checker form non-interactively. @@ -589,6 +602,15 @@ facilisi.""" expected = '\n'.join(['-612', '7720', '2016']) assert_equal(r.body, expected) + def test_variantinfo_ivs(self): + """ + Test the /Variant_info interface used by LOVD2 (with IVS positioning). + """ + r = self.app.get('/Variant_info?LOVD_ver=2.0-33&build=hg19&acc=NM_000249.3&var=c.IVS10%2B3A%3EG') + assert_equal(r.content_type, 'text/plain') + expected = '\n'.join(['884', '3', '884', '3', '37059093', '37059093', 'subst']) + assert_equal(r.body, expected) + def test_upload_local_file(self): """ Test the genbank uploader.