From 6692dcf9abe3ff7ff2ea119cc38209f0a53a1874 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Tue, 20 Aug 2013 11:10:31 -0700 Subject: [PATCH 01/28] Fixed wraparound plots. Refs #3327. Modified BinSortSQL's template to support wraparound plots by adding 360 to field values that wrap around when calculating bins. Removed old wraparound plot code. --- pgd/pgd_search/plot/ConfDistFuncs.py | 34 +++++-------------------- pgd/pgd_search/statistics/aggregates.py | 2 +- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/pgd/pgd_search/plot/ConfDistFuncs.py b/pgd/pgd_search/plot/ConfDistFuncs.py index 6b10e60..11ca847 100644 --- a/pgd/pgd_search/plot/ConfDistFuncs.py +++ b/pgd/pgd_search/plot/ConfDistFuncs.py @@ -251,28 +251,6 @@ def query_bins(self): xbin = self.xbin ybin = self.ybin - # if this is a circular spanning 180/-180 we must adjust the bins - # so that the range falls between -180 and 180 - xlinear = True - if x1 > 180: - x = x1-360 - x1 = 180-self.x - xlinear = False - elif x1 < 0 and x > 0: - x = x1 - x1 = self.x - xlinear = False - - ylinear = True - if y1 > 180: - y = y1-360 - y1 = 180-self.y - ylinear = False - elif y1 < 0 and y > 0: - y = y1 - y1 = self.y - ylinear = False - # Dictionary of bins, keyed by a tuple of x-y coordinates in field units # i.e. (, ) self.bins = {} @@ -290,15 +268,15 @@ def query_bins(self): (Q(**{ '%s__gte'%self.xTextString: x, '%s__lte'%self.xTextString: x1, - }) if xlinear else ( # Altered logic for circular values - Q(**{'%s__gte'%self.xTextString: self.x}) | - Q(**{'%s__lte'%self.xTextString: x}) + }) if x < x1 else ( # Altered logic for circular values + Q(**{'%s__gte'%self.xTextString: x}) | + Q(**{'%s__lte'%self.xTextString: x1}) )) & (Q(**{ '%s__gte'%self.yTextString: y, '%s__lte'%self.yTextString: y1, - }) if ylinear else ( # altered logic for circular values - Q(**{'%s__gte'%self.yTextString: self.y}) | - Q(**{'%s__lte'%self.yTextString: y}) + }) if y < y1 else ( # altered logic for circular values + Q(**{'%s__gte'%self.yTextString: y}) | + Q(**{'%s__lte'%self.yTextString: y1}) )) ) # Total # of observations diff --git a/pgd/pgd_search/statistics/aggregates.py b/pgd/pgd_search/statistics/aggregates.py index 2856700..36259b1 100644 --- a/pgd/pgd_search/statistics/aggregates.py +++ b/pgd/pgd_search/statistics/aggregates.py @@ -62,4 +62,4 @@ class BinSort(PGDAggregate): class BinSortSQL(SQLAggregate): sql_function = '' - sql_template = '%(function)sIF(%(field)s=%(max).16f,FLOOR((%(field)s-%(offset).16f)/%(bincount).16f)-1,FLOOR((%(field)s-%(offset).16f)/%(bincount).16f))' \ No newline at end of file + sql_template = '%(function)sFLOOR(((IF(%(offset).16f>%(max).16fAND%(field)s<0,360,0)+%(field)s-%(offset).16f)/%(bincount).16f)-IF(%(field)s=%(max).16f,1,0))' From 13448a9858e02950f93e64ba44dcead2c6d9ed20 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Tue, 20 Aug 2013 11:41:36 -0700 Subject: [PATCH 02/28] Fixed case when max > 0 and min < max. The check for field being less than 0 was changed to field being less than min which should work for any range. --- pgd/pgd_search/statistics/aggregates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgd/pgd_search/statistics/aggregates.py b/pgd/pgd_search/statistics/aggregates.py index 36259b1..ac60999 100644 --- a/pgd/pgd_search/statistics/aggregates.py +++ b/pgd/pgd_search/statistics/aggregates.py @@ -62,4 +62,4 @@ class BinSort(PGDAggregate): class BinSortSQL(SQLAggregate): sql_function = '' - sql_template = '%(function)sFLOOR(((IF(%(offset).16f>%(max).16fAND%(field)s<0,360,0)+%(field)s-%(offset).16f)/%(bincount).16f)-IF(%(field)s=%(max).16f,1,0))' + sql_template = '%(function)sFLOOR(((IF(%(offset).16f>%(max).16fAND%(field)s<%(offset).16f,360,0)+%(field)s-%(offset).16f)/%(bincount).16f)-IF(%(field)s=%(max).16f,1,0))' From b8e683824f6b8c5a6d1f27a73149019428f76645 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Tue, 20 Aug 2013 13:59:59 -0700 Subject: [PATCH 03/28] Improved BinSort SQL aggregate. Refs #3327. Removed unnecessary comparison in aggregate template. --- pgd/pgd_search/statistics/aggregates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgd/pgd_search/statistics/aggregates.py b/pgd/pgd_search/statistics/aggregates.py index ac60999..2a0b40b 100644 --- a/pgd/pgd_search/statistics/aggregates.py +++ b/pgd/pgd_search/statistics/aggregates.py @@ -62,4 +62,4 @@ class BinSort(PGDAggregate): class BinSortSQL(SQLAggregate): sql_function = '' - sql_template = '%(function)sFLOOR(((IF(%(offset).16f>%(max).16fAND%(field)s<%(offset).16f,360,0)+%(field)s-%(offset).16f)/%(bincount).16f)-IF(%(field)s=%(max).16f,1,0))' + sql_template = '%(function)sFLOOR((IF(%(field)s<%(offset).16f,360,0)+%(field)s-%(offset).16f)/%(bincount).16f)-IF(%(field)s=%(max).16f,1,0)' From e7c217e356650421061ca4c97d1f4486a808e2a1 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Tue, 20 Aug 2013 14:24:24 -0700 Subject: [PATCH 04/28] Fixed bin rendering. Refs #3327. Missed this piece of failed wraparound code from rendering bins. --- pgd/pgd_search/plot/ConfDistFuncs.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/pgd/pgd_search/plot/ConfDistFuncs.py b/pgd/pgd_search/plot/ConfDistFuncs.py index 11ca847..1c6d470 100644 --- a/pgd/pgd_search/plot/ConfDistFuncs.py +++ b/pgd/pgd_search/plot/ConfDistFuncs.py @@ -467,16 +467,8 @@ def Plot(self): hashsize = 10*ratio # calculate bin count and sizes. - if x>0 and x1<0: - # account for circular coordinates - xBinCount = math.ceil((360.0+x1-x)/xbin) - else: - xBinCount = math.ceil((float(x1)-x)/xbin) - if y>0 and y1<0: - # account for circular coordinates - yBinCount = math.ceil((360.0+y1-y)/ybin) - else: - yBinCount = math.ceil((float(y1)-y)/ybin) + xBinCount = math.ceil(((360.0 if x > x1 else 0.0) + x1 - x)/xbin) + yBinCount = math.ceil(((360.0 if y > y1 else 0.0) + y1 - y)/ybin) # determine graph and bin sizes. bins should always fall within the # borders of the graph, and the graph should always exactly fit the From 386d5249628677d4130632830f6742079cf3daee Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Wed, 21 Aug 2013 09:32:24 -0700 Subject: [PATCH 05/28] Updated news page to reflect bugfix. This bugfix was user-visible so it goes on the news page. --- pgd/templates/news.html | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pgd/templates/news.html b/pgd/templates/news.html index 9d6913b..97ac955 100644 --- a/pgd/templates/news.html +++ b/pgd/templates/news.html @@ -22,10 +22,14 @@ {% block content %} -

News

+

News

  • + Version 1.1: released 2013 Nov 11 +
      +
    • Wraparound plots have been fixed (#3327)
    • +
    Version 1.0.1: released 2013 Aug 19
    • Use population standard deviation instead of sample when generating plots (#14613)
    • From 0b09e19bca1c9a1448e5ff3ad55ffc52c1acf2b4 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Wed, 21 Aug 2013 10:45:50 -0700 Subject: [PATCH 06/28] Updated static plot image to reflect updates. A previous bugfix corrected the 180/-180 issues on plots, so a new plot was generated to replace the old example plot. --- ...hi-vrs-psi-of-the-average-N-CA-C-angle.png | Bin 27173 -> 23223 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pgd/static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png b/pgd/static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png index a0b30ec7e8358eee198c30f5d07556247f37293f..531273868c7cd9f23d136928482b63418c98923e 100644 GIT binary patch literal 23223 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 literal 27173 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%0DuOiTrk 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 From 79f6c69baaab31c8382c1dd68f1112f9b1c00010 Mon Sep 17 00:00:00 2001 From: Justin Noah Date: Tue, 15 Oct 2013 11:35:29 -0700 Subject: [PATCH 07/28] database update: Added Oct. 08 2013 update news --- pgd/templates/news.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pgd/templates/news.html b/pgd/templates/news.html index e9974fd..3550873 100644 --- a/pgd/templates/news.html +++ b/pgd/templates/news.html @@ -43,6 +43,14 @@

      News

    • Standard deviations are now calculated correctly on the search statistics page (#12459)
  • +
  • + Database: updated 2013 Oct 08 +
      +
    • 23719 Proteins
    • +
    • 715 New
    • +
    • 70 Removed
    • +
    +
  • Database: updated 2013 Jul 01
      From 145f0af43eec7fde0dedd1320b4f617a1fa62df7 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Thu, 9 Jan 2014 16:46:03 -0800 Subject: [PATCH 08/28] Transitioned from cairo to cairocffi. Added import statements as appropriate, and also changed requirements.txt to include reference to cairocffi. Documentation will need to be changed after this patch is committed. refs #15621 --- pgd/pgd_search/histogram/Histogram.py | 4 ++-- pgd/pgd_search/histogram/svg.py | 4 ++-- pgd/pgd_search/plot/svg.py | 4 ++-- pgd/requirements.txt | 1 + 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pgd/pgd_search/histogram/Histogram.py b/pgd/pgd_search/histogram/Histogram.py index 0d31b66..a118fe9 100644 --- a/pgd/pgd_search/histogram/Histogram.py +++ b/pgd/pgd_search/histogram/Histogram.py @@ -1,6 +1,6 @@ import math -import cairo +import cairocffi as cairo from django.db import connections from django.db.backends.mysql.compiler import SQLCompiler from django.db.models import Max, Min, Count @@ -210,4 +210,4 @@ def render_bars(self, svg): 1, '#000000', '#2c6a22' - ) \ No newline at end of file + ) diff --git a/pgd/pgd_search/histogram/svg.py b/pgd/pgd_search/histogram/svg.py index 48d757a..d5ab6df 100644 --- a/pgd/pgd_search/histogram/svg.py +++ b/pgd/pgd_search/histogram/svg.py @@ -1,4 +1,4 @@ -import cairo +import cairocffi as cairo """ Set of classes for helping deal with SVG graphics """ @@ -116,4 +116,4 @@ def RGBTuple(rgbString): red = int(sub[:2],16)/255.0 green = int(sub[2:4],16)/255.0 blue = int(sub[4:], 16)/255.0 - return (red,green,blue) \ No newline at end of file + return (red,green,blue) diff --git a/pgd/pgd_search/plot/svg.py b/pgd/pgd_search/plot/svg.py index f42a510..2d556f5 100644 --- a/pgd/pgd_search/plot/svg.py +++ b/pgd/pgd_search/plot/svg.py @@ -1,6 +1,6 @@ from math import radians -import cairo +import cairocffi as cairo """ Set of classes for helping deal with SVG graphics """ @@ -122,4 +122,4 @@ def RGBTuple(rgbString): red = int(sub[:2],16)/255.0 green = int(sub[2:4],16)/255.0 blue = int(sub[4:], 16)/255.0 - return (red,green,blue) \ No newline at end of file + return (red,green,blue) diff --git a/pgd/requirements.txt b/pgd/requirements.txt index 9a69e3f..395b950 100644 --- a/pgd/requirements.txt +++ b/pgd/requirements.txt @@ -3,3 +3,4 @@ django-registration south django>=1.3,<1.4 selenium +cairocffi From 44ffa67ae3e1eac349833583163ef36bc96494c6 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Thu, 9 Jan 2014 16:54:21 -0800 Subject: [PATCH 09/28] Modified requirements.txt as per bug report. The two missing lines were added, and the registration line was changed. refs #15615 --- pgd/requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pgd/requirements.txt b/pgd/requirements.txt index 9a69e3f..effd207 100644 --- a/pgd/requirements.txt +++ b/pgd/requirements.txt @@ -1,5 +1,7 @@ biopython -django-registration +django-registration==0.7 south django>=1.3,<1.4 selenium +mysql-python +numpy From 04d570e439c334352df392624458156259f83fa4 Mon Sep 17 00:00:00 2001 From: Justin Noah Date: Thu, 16 Jan 2014 09:45:23 -0800 Subject: [PATCH 10/28] requirements: added simplejson to the list refs #15615 --- pgd/requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/pgd/requirements.txt b/pgd/requirements.txt index effd207..65b1b52 100644 --- a/pgd/requirements.txt +++ b/pgd/requirements.txt @@ -5,3 +5,4 @@ django>=1.3,<1.4 selenium mysql-python numpy +simplejson From 88a98048f2be9b540971cd409e2cb865a3b9ce96 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Mon, 17 Mar 2014 16:35:58 -0700 Subject: [PATCH 11/28] First pass at adding documentation to repository. Added Sphinx to requirements and created the required directory structure. refs #15999 --- pgd/docs/Makefile | 177 ++++++++++++++++++++++++++ pgd/docs/source/conf.py | 258 ++++++++++++++++++++++++++++++++++++++ pgd/docs/source/index.rst | 22 ++++ pgd/requirements.txt | 1 + 4 files changed, 458 insertions(+) create mode 100644 pgd/docs/Makefile create mode 100644 pgd/docs/source/conf.py create mode 100644 pgd/docs/source/index.rst diff --git a/pgd/docs/Makefile b/pgd/docs/Makefile new file mode 100644 index 0000000..e01c730 --- /dev/null +++ b/pgd/docs/Makefile @@ -0,0 +1,177 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ProteinGeometryDatabase.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ProteinGeometryDatabase.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/ProteinGeometryDatabase" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ProteinGeometryDatabase" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/pgd/docs/source/conf.py b/pgd/docs/source/conf.py new file mode 100644 index 0000000..0eac1ed --- /dev/null +++ b/pgd/docs/source/conf.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +# +# Protein Geometry Database documentation build configuration file, created by +# sphinx-quickstart on Mon Mar 17 16:27:11 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys +import os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Protein Geometry Database' +copyright = u'2014, Oregon State University Open Source Lab' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.0.2' +# The full version, including alpha/beta/rc tags. +release = '1.0.2' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'ProteinGeometryDatabasedoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'ProteinGeometryDatabase.tex', u'Protein Geometry Database Documentation', + u'Oregon State University Open Source Lab', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'proteingeometrydatabase', u'Protein Geometry Database Documentation', + [u'Oregon State University Open Source Lab'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'ProteinGeometryDatabase', u'Protein Geometry Database Documentation', + u'Oregon State University Open Source Lab', 'ProteinGeometryDatabase', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/pgd/docs/source/index.rst b/pgd/docs/source/index.rst new file mode 100644 index 0000000..ab4f70a --- /dev/null +++ b/pgd/docs/source/index.rst @@ -0,0 +1,22 @@ +.. Protein Geometry Database documentation master file, created by + sphinx-quickstart on Mon Mar 17 16:27:11 2014. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Protein Geometry Database's documentation! +===================================================== + +Contents: + +.. toctree:: + :maxdepth: 2 + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` + diff --git a/pgd/requirements.txt b/pgd/requirements.txt index 9a69e3f..7d71d84 100644 --- a/pgd/requirements.txt +++ b/pgd/requirements.txt @@ -3,3 +3,4 @@ django-registration south django>=1.3,<1.4 selenium +Sphinx From 8e0ea4035c66f72150aa00544d77fe6603486805 Mon Sep 17 00:00:00 2001 From: Mara Goossens Date: Tue, 24 Jun 2014 14:28:26 -0700 Subject: [PATCH 12/28] Copied the wiki to sphinx refs #15999 Copied all of the code to rst files. Formatted them to hopefully look like the wiki, but haven't actually seen how they look, yet. --- pgd/docs/source/browse.rst | 19 +++ pgd/docs/source/code_modules.rst | 25 ++++ pgd/docs/source/data_dump.rst | 25 ++++ pgd/docs/source/denormalized_search_table.rst | 13 ++ pgd/docs/source/developing_splicer.rst | 38 ++++++ pgd/docs/source/development_workflow.rst | 37 ++++++ pgd/docs/source/hotfix_workflow.rst | 44 +++++++ pgd/docs/source/importing_data.rst | 113 +++++++++++++++++ pgd/docs/source/in_memory_tables.rst | 56 +++++++++ pgd/docs/source/index.rst | 31 ++++- pgd/docs/source/installation.rst | 114 +++++++++++++++++ pgd/docs/source/management_command_design.rst | 36 ++++++ pgd/docs/source/optimization.rst | 10 ++ pgd/docs/source/processor_subtask.rst | 117 ++++++++++++++++++ pgd/docs/source/protein_data_models.rst | 82 ++++++++++++ pgd/docs/source/ramachandran_plot.rst | 64 ++++++++++ pgd/docs/source/release_workflow.rst | 47 +++++++ pgd/docs/source/running_splicer.rst | 52 ++++++++ .../running_splicer_from_command_line.rst | 112 +++++++++++++++++ pgd/docs/source/search_statistics.rst | 58 +++++++++ pgd/docs/source/search_workflow.rst | 78 ++++++++++++ pgd/docs/source/site_specific_information.rst | 55 ++++++++ pgd/docs/source/splicer.rst | 52 ++++++++ pgd/docs/source/sql_aggregate_functions.rst | 62 ++++++++++ pgd/docs/source/sql_indexes.rst | 63 ++++++++++ pgd/docs/source/technologies.rst | 37 ++++++ pgd/docs/source/terminology.rst | 39 ++++++ pgd/docs/source/updating_protein_database.rst | 59 +++++++++ 28 files changed, 1536 insertions(+), 2 deletions(-) create mode 100644 pgd/docs/source/browse.rst create mode 100644 pgd/docs/source/code_modules.rst create mode 100644 pgd/docs/source/data_dump.rst create mode 100644 pgd/docs/source/denormalized_search_table.rst create mode 100644 pgd/docs/source/developing_splicer.rst create mode 100644 pgd/docs/source/development_workflow.rst create mode 100644 pgd/docs/source/hotfix_workflow.rst create mode 100644 pgd/docs/source/importing_data.rst create mode 100644 pgd/docs/source/in_memory_tables.rst create mode 100644 pgd/docs/source/installation.rst create mode 100644 pgd/docs/source/management_command_design.rst create mode 100644 pgd/docs/source/optimization.rst create mode 100644 pgd/docs/source/processor_subtask.rst create mode 100644 pgd/docs/source/protein_data_models.rst create mode 100644 pgd/docs/source/ramachandran_plot.rst create mode 100644 pgd/docs/source/release_workflow.rst create mode 100644 pgd/docs/source/running_splicer.rst create mode 100644 pgd/docs/source/running_splicer_from_command_line.rst create mode 100644 pgd/docs/source/search_statistics.rst create mode 100644 pgd/docs/source/search_workflow.rst create mode 100644 pgd/docs/source/site_specific_information.rst create mode 100644 pgd/docs/source/splicer.rst create mode 100644 pgd/docs/source/sql_aggregate_functions.rst create mode 100644 pgd/docs/source/sql_indexes.rst create mode 100644 pgd/docs/source/technologies.rst create mode 100644 pgd/docs/source/terminology.rst create mode 100644 pgd/docs/source/updating_protein_database.rst diff --git a/pgd/docs/source/browse.rst b/pgd/docs/source/browse.rst new file mode 100644 index 0000000..cc680b5 --- /dev/null +++ b/pgd/docs/source/browse.rst @@ -0,0 +1,19 @@ +****** +Browse +****** + +Browse displays all residues for each segment in a result set. Results a + +============== +Selecting Data +============== + +Queries using Django's ORM focus on a single object. Accessing related fields such as **Residue.prev** or **Residue.next** result in a second query to resolve those objects. This means to display a single segment of length 5 you must do 4 additional queries. To display 25 segments per page would require 125 additional queries. + +The solution is to calculate the list of residues for each segment and select +them by range. For example, to retrieve a segment of length 3 where i=3 select all residues from i-1 to i+1 (2 through 4). :: + + Select * from pgd_core_residue where chainIndex between 2 and 4 + +This allows all residues in a segment to be retrieved in a single query. This +reduces the number of queries to the number of residues in the result set. This may still be a large number of queries but each page is limited to 25 segments per page. diff --git a/pgd/docs/source/code_modules.rst b/pgd/docs/source/code_modules.rst new file mode 100644 index 0000000..ea712a9 --- /dev/null +++ b/pgd/docs/source/code_modules.rst @@ -0,0 +1,25 @@ +============ +Code Modules +============ + +The Protein Geometry Database makes use of Django "apps". Apps are synonymous with a module or plugin. They are module bits of an application that are, for the most part, portable between other apps. + +PGD is divided into three apps to allow code to be portable. + +-------- +PGD Core +-------- + +PGD Core defines the core data structures. There is no functionality contained within this app. It is intended to contain only the database so that it can be reused in other applications + +---------- +PGD Search +---------- + +PGD Search contains logic for searching and displaying data from PGD Core. This includes search logic, models, and the web front end. + +----------- +PGD Splicer +----------- + +PGD Splicer contains all of the code required for importing data from PDB files into objects defined within PGD Core diff --git a/pgd/docs/source/data_dump.rst b/pgd/docs/source/data_dump.rst new file mode 100644 index 0000000..a746a46 --- /dev/null +++ b/pgd/docs/source/data_dump.rst @@ -0,0 +1,25 @@ +********* +Data Dump +********* + +Data dump is a dump of all fields of all segments returned by a search. + +============== +Selecting Data +============== + +Queries using Django's ORM focus on a single object. Accessing related fields such as **Residue.prev** or **Residue.next** result in a second query to resolve those objects. This means to display a single segment of length 5 you must do 4 additional queries. + +The solution is to calculate the list of residues for each segment and select them by range. For example, to retrieve a segment of length 3 where i=3 select all residues from i-1 to i+1 (2 through 4). :: + + Select * from pgd_core_residue where chainIndex between 2 and 4 + +This allows all residues in a segment to be retrieved in a single query. This reduces the number of queries to the number of residues in the result set. This may still be a large number of queries but they may be run in parallel and the buffered nature of datadump spreads them out. + +================= +Buffered Response +================= + +Datadump uses an iterable class which uses threads to buffer data. The buffering threads use a paginator to split a result set into pieces. This allows downloading to start almost immediately after the button is clicked, rather than waiting for the entire dump to be written to memory. + +This is not a perfect solution. Python uses green threads so threads are not truly concurrent. Threads will often be starved and may simply alternate between filling the buffer and emptying it. This is mainly an issue with the threads fighting over the lock. There may be a better solution using double-buffering. diff --git a/pgd/docs/source/denormalized_search_table.rst b/pgd/docs/source/denormalized_search_table.rst new file mode 100644 index 0000000..475254a --- /dev/null +++ b/pgd/docs/source/denormalized_search_table.rst @@ -0,0 +1,13 @@ +**************************************************** +Attempted Optimization: De-normalizing Residue Table +**************************************************** + +Searching segments of residues requires joining the residue table on itself, numerous times. Even when indexed properly joins can be slow. To remove the need for joins we de-normalized **pgd_core_residue** into **pgd_search_segment**. This table contained all properties, for each residue, for each possible segment in a protein. + +This optimization greatly out-performed joins with less than 20,000 results. However, when the result set increased to 300,000 or more results it was twice as slow. The reason was that the table was too large to perform an entire table scan. It only was sped up when there was an index touching every field a search had a clause for. It was not feasible to build an index touch all 200+ fields to allow quick searching. + +========== +Table Size +========== + +The table size was not completely unmanageable, it was only 7 gigabytes. This was fine for on-disk, but would not scale well in memory. diff --git a/pgd/docs/source/developing_splicer.rst b/pgd/docs/source/developing_splicer.rst new file mode 100644 index 0000000..ef0be29 --- /dev/null +++ b/pgd/docs/source/developing_splicer.rst @@ -0,0 +1,38 @@ +****************** +Developing Splicer +****************** + +These are some special instructions for developing splicer components. + +=============== +Django Settings +=============== + +Django and its ORM can be used outside of its webserver. The only requirements are + * the directory containing **settings.py** is on the sys.path + * environment variable **DJANGO_SETTINGS_MODULE** is set to the **settings** + +Splicer components should all do this automatically using python code to add the correct directory to **sys.path**. This works as long as the components are run from the directory containing **settings.py** :: + + import os, sys + #python magic to add the current directory to the pythonpath + sys.path.append(os.getcwd()) + + # ========================================================== + # Setup django environment + # ========================================================== + if not os.environ.has_key('DJANGO_SETTINGS_MODULE'): + os.environ['DJANGO_SETTINGS_MODULE'] = 'settings + # ========================================================== + # Done setting up django environment + # ========================================================== + +======================================== +Running Components from the command line +======================================== + +Splicer Subtasks all contain **main** code that starts the tasks using arguments passed in. All should give you a list of required arguments if you do not pass them in. The main intention of this is to allow easier debugging on tasks. + +For example **ProcessPDBTask** requires a pdb code followed by resolution, threshold, rfactor, and rfree. For testing purposing the properties do not need to be real values. :: + + ~/pgd/pgd $ ./pgd_splicer/ProcessPDBTask.py 12SB 1 2 3 4 diff --git a/pgd/docs/source/development_workflow.rst b/pgd/docs/source/development_workflow.rst new file mode 100644 index 0000000..32e8a42 --- /dev/null +++ b/pgd/docs/source/development_workflow.rst @@ -0,0 +1,37 @@ +=================================================================== +Create an issue in the issue tracker if one does not already exist. +=================================================================== + +All modifications to the source code should be associated with an issue for tracking purposes. + +================================================================== +Create a branch from the develop branch based on the issue number. +================================================================== + +The branch should be named based on the issue type and number. For now, all issues are considered bugs for naming purposes. The branch for issue #14109 (a feature) should be named 'bug/14109'. At this time, the issue should be updated with the status 'In Progress'. + +===================================================================== +Write the code, including tests and release notes entry if necessary. +===================================================================== + +If the branch is fixing a bug, then a test should be written first if at all possible to confirm the bug exists as well as confirm that the bug fix works. + +If the branch makes any user-visible changes, then the release notes (news.html) should be updated to reflect the changes. If you are the first developer to post release notes for the latest development version, add an appropriate header above the existing release notes following the example format -- the release engineer will clean it up if necessary when making the next release. + +================================= +Commit code and update the issue. +================================= + +If any billable work is done on an issue, then code should be committed, and the issue should be updated with the number of billable hours spent on the task and a summary of the work that was done. Commits and updates should happen whenever major subtasks are completed and at close of business. + +==================== +Request code review. +==================== + +When the code is complete, the tests if any all pass, and the release notes entry has been added if necessary, mark the issue 'Needs Review' and assign it to another team member for review. That individual will review the code, the tests, the entry, and any other associated changes for accuracy and consistency. If the branch is acceptable, the reviewer will mark the issue 'Resolved'. If the branch is unacceptable, the reviewer will mark the issue 'Needs Work'. In both cases, the reviewer will update the issue with relevant information and reassign the issue back to the original developer. + +=========================================== +Merge changes back into the develop branch. +=========================================== + +Once the branch passes review, it should be merged back into develop using 'git merge --no-ff' and then develop should be pushed back into the origin. Once this is complete, the devloper should update the issue to that effect. diff --git a/pgd/docs/source/hotfix_workflow.rst b/pgd/docs/source/hotfix_workflow.rst new file mode 100644 index 0000000..f3d9c2c --- /dev/null +++ b/pgd/docs/source/hotfix_workflow.rst @@ -0,0 +1,44 @@ +============================================================ +One or more show-stopping bugs are detected between releases +============================================================ + +An example of a show-stopping bug would be something like #14613 where an unexpected side-effect of a bugfix caused incomplete results to be returned on unrelated searches. + +============================================ +Create a hotfix branch off the master branch +============================================ + +The hotfix branch name will take the form hotfix/x.y.z+1 + +If the current release is 1.2 the hotfix branch is then hotfix/1.2.1 +If the current release is 3.7.2 the hotfix branch is then hotfix/3.7.3 + +========================================= +Create bug branches off the hotfix branch +========================================= + +For each bug that must be fixed, an issue is created and a bug branch named after that issue like all other bugs is created from the hotfix branch instead of the develop branch like all other bugs. + +============================================================= +Merge resolved bugs back to hotfix branch and test on staging +============================================================= + +As each individual bug is resolved, its branch is merged back into the hotfix branch. The hotfix branch can then be updated on the staging server for testing. + +============================================================================= +When all bugs are resolved and merged, merge hotfix branch into master branch +============================================================================= + +Do not forget to increment the version, create a new tag, and update the news page with all bug fixes! + +============================================ +Pull master on production and restart Apache +============================================ + +This should make the new version accessible to the user community. + +===================================== +Merge hotfix branch back into develop +===================================== + +Once production is back up and running, take the time to merge the hotfix branch back into develop. diff --git a/pgd/docs/source/importing_data.rst b/pgd/docs/source/importing_data.rst new file mode 100644 index 0000000..d3be88c --- /dev/null +++ b/pgd/docs/source/importing_data.rst @@ -0,0 +1,113 @@ +===================================== +Running Splicer From The Command Line +===================================== + +Splicer can be run from the command line. It requires that several steps be run separately. + +All commands should be run from the project root (directory with settings.py in it). + +^^^^^^^^^^^^^^^^^^ +Selecting Proteins +^^^^^^^^^^^^^^^^^^ + +Proteins must first be selected. Default filtering settings will be used for threshold, resolution and r_factor. :: + + 1 ./pgd_splicer/dunbrack_selector.py + +This will return information about the the parameters used, the files proteins were selected from, and a list of proteins in the following format: :: + + code chains threshold resolution rfactor rfree + +Save the selection into a file:: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt + +------- +Options +------- + + * --pipeout - will only output the data. This should be used if you would like to create output suitable for input into one of the other steps + +^^^^^^^^^^^^^^^^^^^^^ +Downloading PDB Files +^^^^^^^^^^^^^^^^^^^^^ + +PDB files are downloaded from an FTP site using **ftpupdate.py**. This script will synchronize **./pdb** with the remote FTP server. Only new files will be downloaded, but it will check the timestamps on all files. + +This is a time-consuming step. Be prepared to wait for approximately two days for this to complete on a fresh local copy, or one day on an update. :: + + 1 ./pgd_splicer/ftpupdate.py code [code...] + +To only grab the proteins which are selected (and cut down massively on consumed bandwidth and time), try:: + + 1./pgd_splicer/ftpupdate.py --pipein < selection.txt + +^^^^^^^^^^^^^^^^^^^^ +Processing PDB Files +^^^^^^^^^^^^^^^^^^^^ + +PDB files can be imported into the database with **ProcessPDBTask.py**. Multiple proteins can be fed as commands to be imported. Errors will be written to **ProcessPDB.log**. :: + + 1 ./pgd_splicer/ProcessPDBTask.py code chains threshold resolution rfactor rfree [repeat] + +As before, a selection can be piped in:: + + 1 ./pgd_splicer/ProcessPDBTask.py --pipein < selection.txt + +Expect this to take a few days as well. + +---------- +Parameters +---------- + +Parameters are all required, and may be repeated for multiple proteins. + + * **code** - protein code to import, should be all uppercase + * **chains** - list of chains to import, should be a string of chain ids. (ie. ABCDEF). The string should not have quotes around it. + * **threshold, resolution, rfactor, rfree** - the value for these fields. These properties are retrieved from the selection script so they are included as input for processing the protein. + +------- +Options +------- + + * --pipein - input will be read from a pipe instead of arguments. proteins in the list should be separated by newlines. + +======= +Example +======= + +Some examples. Intermediate output is saved to a text file so that it can be examined later. + +^^^^^^^^^^^ +Full Import +^^^^^^^^^^^ + +Update all proteins regardless of whether the file was downloaded by **ftpupdate**. *ProcessPDBTask* will still check the update date and exclude pdbs that are not new. :: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2 ./pgd_splicer/ftpupdate.py --pipein < selected_proteins.txt + 3 ./pgd_splicer/ProcessPDBTask.py --pipein < selected_proteins.txt + +^^^^^^^^^^^^^^^ +Update Only New +^^^^^^^^^^^^^^^ + +Update only proteins for which we have a new FTP file. :: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2 ./pgd_splicer/ftpupdate.py --pipein --pipeout < selected_proteins.txt > updated_proteins.txt + 3 ./pgd_splicer/ProcessPDBTask.py --pipein < updated_proteins.txt + +^^^^^^^^^^^^^^^^^^^^^^^^ +Update Skipping Download +^^^^^^^^^^^^^^^^^^^^^^^^ + +If all the pdb files are already downloaded you may skip the FTP step to save time. :: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2 ./pgd_splicer/ProcessPDBTask.py --pipein < selected_proteins.txt + +or as a single command:: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout | ./pgd_splicer/ProcessPDBTask.py --pipein + diff --git a/pgd/docs/source/in_memory_tables.rst b/pgd/docs/source/in_memory_tables.rst new file mode 100644 index 0000000..697199c --- /dev/null +++ b/pgd/docs/source/in_memory_tables.rst @@ -0,0 +1,56 @@ +****************************** +Optimization: In Memory Tables +****************************** + +Loading PGD tables in memory, if indexed properly, will greatly outperform on-disk tables. It is a solution dependent on a capable enough server. + +====================== +Indexing Memory Tables +====================== + +Memory tables will only outperform a properly indexed on-disk table if it is also indexed. Above a certain threshold a full table scan of a memory table is still slower than a binary search of an index, even when it is on disk. + +Memory tables support indexing, but mysql does not correctly index when using btree. Btree is the default for on-disk tables, and is generally faster than a Hash index. however, mysql will generate an empty index when btree is used with memory tables. This means that any table joined with a memory table must also be in memory. + +========================== +Parallelization of Queries +========================== + +Memory addresses can be read by multiple threads simultaneously, unlike disks which require seeking back and forth. Without seek times to slow down simultaneous reads, multiple queries can be run on an in memory table at the same time. This can reduce the time required for a query to the longest in the set of queries + +**Diagram** + +Note that this becomes a limitation of CPU cores and the number of concurrent threads the server is capable of handling. + +======================================= +Startup and Django Configuration Issues +======================================= + +Memory tables do not persist through mysql restarts. They must be recreated and indexed every time the server starts. This needs to be automated so that when the server first starts it is able to check and create the tables if needed. + +Django must also be told what this special table is. The choices are: + + * rename the table during the creation process. It might be impossible to determine the state of the tables though. + * change the name of the table in the django configuration to match the in memory table. this can only be done prior to django loading, the django orm cannot be reinitialized + +=============== +Growth Concerns +=============== + +Growth of the database is a major concern when dealing with large memory tables. Ram is cheap, but not as cheap as disk space. The current PGD database requries about 1.5 gigabytes (1.1 for Residues) of ram to load the Protein and Residue table in memory. Two factors will increase growth: + + * Additional Fields - We have at least a 2 dozen new properties to add, which will add around 80% growth in the short term. There may be more fields added later also. + * Additional Proteins added - Expected to be 10% growth per year. + +The works out to the following projects: + + * current - 1.5 gigabytes + * 6 Months - 2.4 gigabytes + * 12 Months - 2.64 gigabytes + * 2 Years - 2.9 gigabytes + * 3 Years - 3.19 gigabytes + * 4 Years - 3.51 gigabytes + * 5 Years - 3.86 gigabytes + +A server purchased now with 4 gigabytes of ram allocated for the just the memory table would last 5 years. This is two years past what is normally "end of life" for a server. + diff --git a/pgd/docs/source/index.rst b/pgd/docs/source/index.rst index ab4f70a..d8f2608 100644 --- a/pgd/docs/source/index.rst +++ b/pgd/docs/source/index.rst @@ -9,8 +9,35 @@ Welcome to Protein Geometry Database's documentation! Contents: .. toctree:: - :maxdepth: 2 - + :maxdepth: 3 + + installation + importing_data + updating_protein_database + site_specific_information + technologies + terminology + code_modules + protein_data_models + optimization + sql_indexes + sql_aggregate_functions + in_memory_tables + denormalized_search_table + search_workflow + ramachandran_plot + search_statistics + data_dump + browse + splicer + developing_splicer + processor_subtask + running_splicer + running_splicer_from_command_line + development_workflow + hotfix_workflow + release_workflow + management_command_design Indices and tables diff --git a/pgd/docs/source/installation.rst b/pgd/docs/source/installation.rst new file mode 100644 index 0000000..12125b5 --- /dev/null +++ b/pgd/docs/source/installation.rst @@ -0,0 +1,114 @@ +******************************* +Development Branch Installation +******************************* + +Dependencies:: + libmysqlclient-dev + +In a virtualenv:: + pip install -r pgd/requirements.txt + +************************** +Master Branch Installation +************************** +This is a manual installation guide for the Protein Geometry Database + +^^^^^^^^^^^^^^^^^^^^ +Install dependencies +^^^^^^^^^^^^^^^^^^^^ + +The following packages are required to run Protein Geometry Database. Either install these with your system's package manager or from `Pip `_ (recommended). Note that system packages may not have the correct version. + + * `Python >= 2.5 but < 3.x `_ (Python 3.x is not supported due to backward-compatibility issues) + * `setuptools >= 0.6.28 `_ + * `simplejson `_ + * `MySQL-python `_ + * `numpy `_ + * `biopython 1.57 `_ + * `Python Django 1.3.x + `_ + * `Python Django-registration ==0.7 + `_ + * `Py2Cairo `_ See instructions below for installing the correct Py2Cairo + * `Selenium `_ for running browser-based tests + +The process for installing "py2cairo" was recently reported to be: + + #. Install libcairo2-dev via apt-get + #. Install simplejson via pip to virtualenv + #. Retrieve py2cairo from https://github.com/dieterv/py2cairo.git + #. Run touch ChangeLog + #. Run ./autogen.sh from that checkout + #. Run ./waf configure --prefix=foo where foo is the absolute location of the virtualenv + #. Run ./waf build + #. Run ./waf install + #. Open a python interpreter and confirm that the cairo module can be imported + +^^^^^^^^^^^^ +Get the Code +^^^^^^^^^^^^ + + 1. Make sure you have Git installed. + 2. Either download and unpack the "latest release":, or check it out from the repository:: + + # For public, read-only access use: + git clone git://git.osuosl.org/gitolite/pgd + + # Developers with push access to the repository use: + git clone git@git.osuosl.org:pgd + +^^^^^^^^^^^^^ +Configuration +^^^^^^^^^^^^^ + + 1. In the project root, you'll find a default-settings file called settings.py.dist. Copy it to settings.py:: + + cp settings.py.dist settings.py + + 2. If you want to use another database engine besides the default SQLite (not recommended for production), edit settings.py, and edit the following lines to reflect your wishes:: + + 1 DATABASE_ENGINE = '' # <-- Change this to 'mysql' + 2 DATABASE_NAME = '' # <-- Change this to a database name, or a file for SQLite + 3 DATABASE_USER = '' # <-- Change this (not needed for SQLite) + 4 DATABASE_PASSWORD = '' # <-- Change this (not needed for SQLite) + 5 DATABASE_HOST = '' # <-- Change this (not needed if database is localhost) + 6 DATABASE_PORT = '' # <-- Change this (not needed if database is localhost) + + 3. Initialize Database:: + + ./manage.py syncdb + + 4. Everything should be all set up! Run the development server with:: + + ./manage.py runserver + +============== +Importing Data +============== + +See the `importing data +`_ section for instructions on how to import data. + +================================================ +Additional configuration for production servers: +================================================ + +Deploying a production server requires additional setup steps. + + 1. Change your SECRET_KEY to unique (and hopefully unguessable) strings in your settings.py. + 2. Ensure the server has the ability to send emails or you have access to an SMTP server. Set EMAIL_HOST, EMAIL_PORT, and DEFAULT_FROM_EMAIL in settings.py. For more complicated outgoing mail setups, please refer to the django email documentation. + 3. Follow the django guide to deploy with apache. Here is an example mod_wsgi file:: + + 1 import os + 2 import sys + 3 + 4 path = '/var/lib/django/pgd' + 5 if path not in sys.path: + 6 sys.path.append(path) + 7 + 8 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + 9 + 10 import django.core.handlers.wsgi + 11 application = django.core.handlers.wsgi.WSGIHandler() + + 4. If New Relic support is required, modify the WSGI file according to the New Relic documentation. On zeus, the New Relic configuration file is /etc/newrelic.ini. diff --git a/pgd/docs/source/management_command_design.rst b/pgd/docs/source/management_command_design.rst new file mode 100644 index 0000000..5a7b461 --- /dev/null +++ b/pgd/docs/source/management_command_design.rst @@ -0,0 +1,36 @@ +***************************************** +Management Commands and Possible Redisign +***************************************** + +2 Databases: + * Staging (aka Silver) + * Master (aka Gold) + +Instead of two databases :: + + class Protein(models.Model): + """ + Same as before + """ + ... + + class GoldProtein(Protein): + """ + Nothing actually goes here + """ + +Manage Cammonds: + + * Import (Modifies Staging, Reads from Master) + * Fetches pdb files (like fetch does currently) + * --fetch-only as an option + * Stores the selection in the Audit table + * Dumps Proteins from staging + * "ProcessPDBTask" + * Generates a diff (Total, New, Removed) data and stores it in the Audit table + + * Promote (Reads Staging, Modifies Master) + * Dumps Data + * Copies Staging into Master + * Updates Master Audit table + diff --git a/pgd/docs/source/optimization.rst b/pgd/docs/source/optimization.rst new file mode 100644 index 0000000..7387620 --- /dev/null +++ b/pgd/docs/source/optimization.rst @@ -0,0 +1,10 @@ +************ +Optimization +************ + +PGD has been optimized using the following techniques + + * `SQL Indexes `_ + * `SQL Aggregate Functions `_ + * `In Memory Tables `_ + * `Denormalized Search Table `_ diff --git a/pgd/docs/source/processor_subtask.rst b/pgd/docs/source/processor_subtask.rst new file mode 100644 index 0000000..6975137 --- /dev/null +++ b/pgd/docs/source/processor_subtask.rst @@ -0,0 +1,117 @@ +********************** +Splicer Processor Task +********************** + +The **Splicer Processor Task**, also known as **ProcessPDBTask** accepts a PDB file and processes it into `PGD Protein Models `_. + +============================= +Running From the command line +============================= + +Like all splicer components *ProcessPDBTask* can be run from the commandline to simplify debugging the task. It requires the follow arguments: + * PDB Code - 4 character alphanumeric code for a protein. + * Threshold - Float + * Resolution - Float + * Rfactor - Float + * Rfree - Float + +When debugging only the PDB code need be a real value. The other values are required, but not validated. + +========= +Libraries +========= + + * `BioPython `_ - A library that can parse PDB files and contains various functions within its `PDB API `_ for extracting data from them. + * DSSP - A program for calculating secondary structure. BioPython has bindings for this program. + +============ +Parsing PDBs +============ + +Some properties are available as simple properties using the `Residue `_ class within BioPython. Most others require calculations involving individual `Atoms `_ within a Residue + +---------------------------- +Parsing Geometric Properties +---------------------------- + +Geometric properties must be calculated from raw atom data. BioPython supplies several functions for calculating functions between atom vectors. + * **calc_length(vector, vector)** - Calculates distance between atoms in 3D space (supplied by PGD) + * `calc_angle `_ - Calculates the angle between 3 atoms. + * `calc_dihedral `_ - Calculates the dihedral (torsion) angle between 4 atoms + +These functions require vectors which can be retrieved using `Atom.get_vector() `_ + +========== +Example a3 +========== + +:: + N = residue['N'].get_vector() + CA = residue['CA'].get_vector() + C = residue['C'].get_vector() + + a3 = calc_angle(N,CA,C) + +============ +Example: Ome +============ + +:: + oldCA = prev_residue['CA'].get_vector() + oldC = prev_residue['C'].get_vector() + N = residue['N'].get_vector() + CA = residue['CA'].get_vector() + + ome = calc_dihedral(oldCA,oldC,N,CA) + +--------------------------- +Parsing Averaged Properties +--------------------------- + +Several properties are presented as averages across the **Main-Chain, *Side-Chain,** and **Carbon Gamma** atoms. + + * **Main-Chain** - atoms N, C-alpha, C, O, OXT + * **Side-Chain** - all other atoms excluding Main-Chain, C-gamma, and HETs (water) + * **Carbon-gamma** - also known as C-gamma or Cg. A single atom. + +This properties are calculated as the average of an atom level property across the atoms in this group. + +============================ +Example B-factor: Bm, Bs, Bg +============================ + +:: + """ + Other B Averages + Bm - Average of bfactors in main chain. + Bm - Average of bfactors in side chain. + """ + main_chain = [] + side_chain = [] + for a in res.child_list: + if a.name in ('N', 'CA', 'C', 'O','OXT'): + main_chain.append(a.get_bfactor()) + elif a.name in ('H'): + continue + else: + side_chain.append(a.get_bfactor()) + + if main_chain != []: + res_dict['bm'] = sum(main_chain)/len(main_chain) + + if side_chain != []: + res_dict['bs'] = sum(side_chain)/len(side_chain) + +----------------------------- +Parsing Side Chain Properties +----------------------------- + +Side chains are different for each type of atom. They require a map of connections to determine which atoms require angles, lengths, and dihedral angles calculated. + +Currently **Chi1, Chi2, Chi3, and Chi4*are calculated using a map in *chi.py**. Sidechain lengths and angles will be added later, requiring an additional map of connections between atoms. + +=============== +Update Checking +=============== + +PDB Processor checks for updates when processing proteins. The `Protein Model `_ contains a timestamp which corresponds to the update timestamp on the PDB file it was imported from. A protein will only be processed if the PDB file is newer. diff --git a/pgd/docs/source/protein_data_models.rst b/pgd/docs/source/protein_data_models.rst new file mode 100644 index 0000000..0bc1696 --- /dev/null +++ b/pgd/docs/source/protein_data_models.rst @@ -0,0 +1,82 @@ +******************* +Protein Data Models +******************* + +.. image:: protein_model.png + +Protein data model is composed of three classes: **Protein, Chain,** and **Residue**. The models are designed to represent the protein in the most compact way. With proper SQL indexes this is also the most efficient method of storing the data for search queries when the database contains greater than 2 million residues. + +The models are defined as Django models meaning the data exists as both Python classes and SQL Tables. This allows the data to be accessed through SQL, or preferably as Python objects through the Django Query API. Django models can be used from any program provided your *Django environment is configured* + +For more information on Django models: + + * `Django Model Reference + `_ + * `Django Query Reference + `_ + +======= +Protein +======= + +Represents a Protein. + + * **code** - 4 letter code for protein as imported from pdb. + * **rfactor** + * **rfree** + * **resolution** + * **threshold** + * **residues** - related field collection, returns queryset of all residues related to this protein + * **chains** - related field colleciton, returns queryset of all chain related to this protein + +===== +Chain +===== + +Chain id's are stored to allow importing of multiple chains from a single protein. The search interface does not currently include selection of chains. + + * **protein** (protein_id) Foreign Key relation to the protein this residue belongs to. May be retrieved as a Protein object or identifier + * **residues** - related field collection, returns queryset of all residues related to this protein + +======= +Residue +======= + +Represents a Residue (Amino Acid) belonging to a protein + + * **id** - unique identifier for residue, internal to database + * **oldID** - identifier as listed in PDB file. May include icodes appended to the end. + * **chainIndex** - numerical index of residue in the chain. All chain breaks are represented as a single gap in numbers. IE. 1,2,4,5. + * **protein** (protein_id) Foreign Key relation to the protein this residue belongs to. May be retrieved as a Protein object or identifier + * **prev** (prev_id) - Foreign Key relation to the previous residue in the chain, this may be retrieved as a Residue object or identifier + * **next** (next_id) - Foreign Key relation to the next residue in the chain, this may be retrieved as a Residue object or identifier + + * **Bond Lengths** + * L1 - + * L2 - + * L3 - + * L4 - + * L5 - + + * **Bond Angles** + * A1 - + * A2 - + * A3 - + * A4 - + * A5 - + * A6 - + * A7 - + + * **Dihedral Angles** + * Omega + * Phi + * Psi + * Zeta + + * **Sidechain** + * **X1 through X4** as defined by mmlib. + + * **B-Factor** + * **Bm** - Average b-factor of mainchain atoms + * **Bs** - Average b-factor of sidechain + * **Bg** - Average b-factor of Cg atom if present diff --git a/pgd/docs/source/ramachandran_plot.rst b/pgd/docs/source/ramachandran_plot.rst new file mode 100644 index 0000000..46c0016 --- /dev/null +++ b/pgd/docs/source/ramachandran_plot.rst @@ -0,0 +1,64 @@ +****************** +Ramachandran Plots +****************** + +PGD provides a pseudo ramachandran plot. Our plot uses square bins rather than a more freeflowing plot. This allows quicker generation of graphs. + +The Plot page uses django querysets, but makes use of [SQL Aggregates]. For ease in explaining how the data is processed this page will refer to the SQL generated by the querysets + +============== +Data Selection +============== + +The Ramachandran plot is generated using a specialized SQL query to group data points into bins. :: + + 1. Each coordinate has the minimum value subtracted and then divided by the BIN_SIZE and rounded down (FLOOR). This calculates the bin coordinate for each residue. + 2. GROUP BY is applied to both the X and Y coordinates. This sorts the residues into a grid. + + select FLOOR((phi-PHI_MIN)/10) as X, FLOOR((psi-PSI_MIN)/10) as Y from pgd_core_residue GROUP BY X, Y + +Subtracting the minimum value from the coordinate shifts the start of the bins to the minimum value. The first bin will always be the same size as other bins. The last bin may be a different size if BIN_SIZE does not divide MAX-MIN evenly + +====================== +Statistics Calculation +====================== + +Once records are selected statistical calculations are performed depending on the input. By default the ramachandran plot z-axis displays observations, or the count of residues in that bin. :: + + select Count(*) as count, FLOOR((phi-PHI_MIN)/10) as X, FLOOR((psi-PSI_MIN)/10) as Y from pgd_core_residue GROUP BY X, Y + +Optionally the z-axis can also display the average of user selected attribute. :: + + select AVG(a1) as avg, STDDEV(a1) as stddev, FLOOR((phi-PHI_MIN)/10) as X, FLOOR((psi-PSI_MIN)/10) as Y from pgd_core_residue GROUP BY X, Y + +======== +Coloring +======== + +Graphs are colored to represent values on a z-axis. + + * **Reference** - The point at which to calculate distance from for determining colors. Used to adjust focus to a specific value. By default reference is the mean. + * **Outlier Sigma** - Number of sigma (standard deviations) beyond which values are considered outliers. Standard deviation is not recalculated excluding outliers, but they are excluded when calculating the range of colors. This helps keep the range of colors from spreading too widely due to outliers far from the mean. + +----------------- +Logarithmic Scale +----------------- + +PGD applies a logarithmic scale to all bins. Logarithmic scaled colors allow a greater range of colors choices to be applied closer to the **Reference**. As values approach the extend of **OUTLIER_SIGMA**** SIGMA* the number of colors to choose from lessens. This allows differences within values close to the mean to be more apparent. + +------------ +Color Ranges +------------ + +Plots are colored using a predefined tuple of RGB values: + + * Max value + * Adjustment value - used to add a minimum value to the color. ie. the Blue plot adds 75 to all blue values, shifting all colors into a blue hue. + +--------- +Algorithm +--------- +:: + 1. The logarithmic scale is first calculated producing a number from 0 to 1. + 2. This is multiplied by each value in the RGB MAX tuple + 3. each adjustment value in the RGB adjustment is added to the corresponding RGB value diff --git a/pgd/docs/source/release_workflow.rst b/pgd/docs/source/release_workflow.rst new file mode 100644 index 0000000..8239d04 --- /dev/null +++ b/pgd/docs/source/release_workflow.rst @@ -0,0 +1,47 @@ +======================================================================== +Announce the upcoming release to the PGD list four weeks before release. +Include the following information: + + * version number to be released in x.y.z format + * rough list of features and bugfixes expected to be included + * schedule of events (feature freeze, release branch, etc.) + +============================================================== +Impose a feature freeze on develop three weeks before release. +============================================================== + +Features are no longer permitted to be merged into the develop branch, only bugs. + +Release engineer checks each resolved ticket to confirm that the branch was indeed merged into the develop branch. + +================================================== +Start the release branch two weeks before release. +================================================== + +Create a branch from develop named 'release/x.y.z' using the version number mentioned in the release announcement. + +Log into the dev site and check out the release branch there. + +Bugfixes can only be made from and returned to this branch at this time. + +================================================== +Freeze the release branch one week before release. +================================================== + +Log into the staging site and check out the release branch there. + +Only emergency fixes allowed at this point! + +============================================================================= +Release the software, close tickets and unfreeze develop on the release date. +============================================================================= + +Merge the release branch back into master and develop branches. + +Log into the production site and check out the master branch there. + +All resolved tickets should be closed at this time. + +Any existing branches should be rebased from develop before development continues. + +Features may now be merged back into develop at this time. diff --git a/pgd/docs/source/running_splicer.rst b/pgd/docs/source/running_splicer.rst new file mode 100644 index 0000000..cf0429a --- /dev/null +++ b/pgd/docs/source/running_splicer.rst @@ -0,0 +1,52 @@ +*************** +Running Splicer +*************** + +Splicer is intended to be run on a `Pydra `_ cluster. These are instructions and notes on running it and dealing with Pydra's immaturity. + +Splicer can be `deployed `_ and `run `_ like any other task on a Pydra cluster. + +Components of splicer can also be `run manually from the command line +`_ + +=============== +Slow FTP Issues +=============== + +The FTP server for PDB files is a very slow, rate limited, server located in the UK. PDB files are currently 1.8 gigabytes total for 16,000 proteins in PGD. It can take a long time to download this much data from the FTP server. This is handled in two ways: + +----------------------------------------- +Maintaining Connections Between Workunits +----------------------------------------- + +Each workunit is composed of downloading and processing a PDB file. Rather than disconnecting from the FTP server, connections are maintained until the last work unit is completed. This removes the overhead for connecting and disconnecting from the server + +-------------------------- +Only Downloading New Files +-------------------------- + +Checking dates is very fast, the MODTIME command completes almost instantly. This prevents uneeded downloading + +-------------------------------- +Storing files in a network share +-------------------------------- + +Pydra can't guarantee that future runs of Splicer will process the same set of proteins on the same hardware. This means that an up-to-date PDB could be mistaken for a PDB that doesn't exist. Storing the files on a shared filesystem ensures that regardless of which `Node `_ is assigned the workunit, it will find the same set of PDB files. + +Note that this only matters when using + +========================== +Workunit Thrashing Problem +========================== + +There is an outstanding `bug in pydra that causes the node to crash when workunits complete too quickly `_. Splicer includes an option to batch process proteins to ensure that this does not happen. Eventually `batching workunits `_ will an automatic feature of Pydra + +When running repeat runs of Pydra it is important to increase the workunit size to at least 500-1000. Because the date checks are very fast it will cycle through the existing proteins very quickly. + +================= +Debugging Splicer +================= + +Pydra `logs `_ most things that happen within it. A full `task history `_ can be viewed by clicking the history icon found on the pydra `tasks page `_. Clicking on a task instance gives you more details about the task including which workunits were successful and what their arguments were. + +Workunits are logged individually and located in **/var/logs/pydra/archive**. The logs are aggregated from the Nodes after it is done with the entire task diff --git a/pgd/docs/source/running_splicer_from_command_line.rst b/pgd/docs/source/running_splicer_from_command_line.rst new file mode 100644 index 0000000..b24317f --- /dev/null +++ b/pgd/docs/source/running_splicer_from_command_line.rst @@ -0,0 +1,112 @@ +************************************* +Running Splicer From The Command Line +************************************* + +Splicer can be run from the command line. It requires that several steps be run separately. + +All commands should be run from the project root (directory with settings.py in it). + +------------------ +Selecting Proteins +------------------ + +Proteins must first be selected. Default filtering settings will be used for threshold, resolution and r_factor. :: + + 1 ./pgd_splicer/dunbrack_selector.py + +This will return information about the the parameters used, the files proteins were selected from, and a list of proteins in the following format:: + + code chains threshold resolution rfactor rfree + +Save the selection into a file:: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt + +======= +Options +======= + + * --pipeout - will only output the data. This should be used if you would like to create output suitable for input into one of the other steps + +--------------------- +Downloading PDB Files +--------------------- + +PDB files are downloaded from an FTP site using **ftpupdate.py**. This script will synchronize **./pdb** with the remote FTP server. Only new files will be downloaded, but it will check the timestamps on all files. + +This is a time-consuming step. Be prepared to wait for approximately two days for this to complete on a fresh local copy, or one day on an update. :: + + 1 ./pgd_splicer/ftpupdate.py code [code...] + +To only grab the proteins which are selected (and cut down massively on consumed bandwidth and time), try:: + + 1 ./pgd_splicer/ftpupdate.py --pipein < selection.txt + +-------------------- +Processing PDB Files +-------------------- + +PDB files can be imported into the database with **ProcessPDBTask.py**. Multiple proteins can be fed as commands to be imported. Errors will be written to ProcessPDB.log :: + + 1 ./pgd_splicer/ProcessPDBTask.py code chains threshold resolution rfactor rfree [repeat] + +As before, a selection can be piped in:: + + 1 ./pgd_splicer/ProcessPDBTask.py --pipein < selection.txt + +Expect this to take a few days as well. + +========== +Parameters +========== + +Parameters are all required, and may be repeated for multiple proteins. + + * code - protein code to import, should be all uppercase + * chains - list of chains to import, should be a string of chain ids. (ie. ABCDEF). The string should not have quotes around it. + * threshold, resolution, rfactor, rfree - the value for these fields. These properties are retrieved from the selection script so they are included as input for processing the protein. + +======= +Options +======= + + * --pipein - input will be read from a pipe instead of arguments. proteins in the list should be separated by newlines. + +======= +Example +======= + +Some examples. Intermediate output is saved to a text file so that it can be examined later. + +----------- +Full Import +----------- + +Update all proteins regardless of whether the file was downloaded by **ftpupdate**. ProcessPDBTask will still check the update date and exclude pdbs that are not new. :: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2 ./pgd_splicer/ftpupdate.py --pipein < selected_proteins.txt + 3 ./pgd_splicer/ProcessPDBTask.py --pipein < selected_proteins.txt + +--------------- +Update Only New +--------------- + +Update only proteins for which we have a new FTP file. :: + + 1./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2./pgd_splicer/ftpupdate.py --pipein --pipeout < selected_proteins.txt > updated_proteins.txt + 3./pgd_splicer/ProcessPDBTask.py --pipein < updated_proteins.txt + +------------------------ +Update Skipping Download +------------------------ + +If all the pdb files are already downloaded you may skip the FTP step to save time. :: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selected_proteins.txt + 2 ./pgd_splicer/ProcessPDBTask.py --pipein < selected_proteins.txt + +or as a single command:: + + 1 ./pgd_splicer/dunbrack_selector.py --pipeout | ./pgd_splicer/ProcessPDBTask.py --pipein diff --git a/pgd/docs/source/search_statistics.rst b/pgd/docs/source/search_statistics.rst new file mode 100644 index 0000000..b6f8dc9 --- /dev/null +++ b/pgd/docs/source/search_statistics.rst @@ -0,0 +1,58 @@ +***************** +Search Statistics +***************** + +The statistics page provides statistics of properties across all results for a specific residue index. Results are grouped by Amino Acid Type, and or Secondary Structure Type. + +======= +Queries +======= + +The statistics page makes use of aggregate functions to calculate values. Because of the different types of grouping and statistics, it requires several queries to retrieve all of the statistics. + +------------------------------------------------ +Secondary Structure Counts and Amino Acid Totals +------------------------------------------------ + +This query produces three values: + + * Counts of residues per Secondary Structure Type, per Amino Acid Type. + * Counts of residues per Amino Acid Type. + * Total count of all residues in search. + +The SQL required is :: + + Select count(*) as count, aa_type, ss_type from pgd_core_residue GROUP BY aa_type, ss_type WITH ROLLUP; + +The **WITH ROLLUP** clause instructs mysql to include totals for the fields. This produces the total per amino acid type, and total count of all residues. WITH ROLLUP only affects the first field in the GROUP BY class so totals per Secondary Structure Type are not produced by this query. + +-------------------------------- +Secondary Structure Total Counts +-------------------------------- + +Count of residues per Secondary Structure Type. :: + + Select count(*) as count, ss_type from pgd_core_residue GROUP BY ss; + +---------------- +Field Statistics +---------------- + +Min, Max, Average, and Standard Deviation are calculated for every residue, per Amino Acid Type, include totals. :: + + SELECT MIN(a1), MAX(a1), AVG(a1), STDDEV(a1) from pgd_core_residue GROUP_BY aa WITH ROLLUP; + +-------------------------- +Dihedral Angles Statistics +-------------------------- + +Dihedral angles (ome, zeta, phi, psi, etc.) require the use of `Special Statistics for Dihedral Angles `_. Average is calculated in the field statistics query, but standard deviation requires a second query. + +============ +Optimization +============ + +Statistics uses the following optimizations: + + * `SQL Aggregate Functions `_ to reduce network transport overhead + * `Parallel Queries `_ to run calculations simultaneously diff --git a/pgd/docs/source/search_workflow.rst b/pgd/docs/source/search_workflow.rst new file mode 100644 index 0000000..58bfcdc --- /dev/null +++ b/pgd/docs/source/search_workflow.rst @@ -0,0 +1,78 @@ +*************** +Search Workflow +*************** + +PGD Search workflow is designed to take advantage of all of the features provided by Django: Models, Forms, and QuerySets. Search Models and Forms are interchangeable via methods provided by PGD. + +====== +Models +====== + +.. image:: search_models.png + +The Search class closely mimics a protein segment. It is composed of a three classes: Search, SearchResidue, and SearchCode. By creating Search as django models they can be stored and retrieved from the database. + +------ +Search +------ + +Search model contains all fields present in a protein. + +---------- +SearchCode +---------- + +SearchCode is a list of protein IDs to include in the search + +------------- +SearchResidue +------------- + +SearchResidue mimics a Residue object. It contains all fields that a + + * Fields are all strings and can use the PGD Query Syntax + * Type fields such as AA_Type and SS_Type are stored as an integer with choices encoded in binary to conserve space. + +===== +Forms +===== + +SearchForms mimic Search Models exactly. + +=========== +Conversions +=========== + +Search and SearchForm are interchangeable. + + * Use **function name** to convert a **Search** to a **!SearchForm** + * Use **function name** to convert a **!SearchForm** to a **Search** + +Search can generate Django QuerySets + + Use **search.queryset()** + +======== +Workflow +======== + +.. image:: query_lifecycle.png + +When submitting the search form, if the search is successful a **Search** object will be created and stored in the user's session. The **Search** can be retrieved and converted back into a **SearchForm** to edit the search. + +.. image:: query_customization.png + +This **Search** object becomes the basis for rendering all other pages. PGD is taking advantage of two features of querysets: + + * **Lazy-execution of SQL Queries** - the query is not performed until the results are requested from the object. + * **Querysets can be further customized** - the query can be refined further to filter the fields returned, statistical calculations, etc. + +These features allow PGD apply view specific logic without recreating the **base query** for every **view**. The **base query** contains the set of records to operate on. The **view** applies its specific logic and calculations. + +-------- +Examples +-------- + + * Plot page only requires 3 properties to perform its calculation + * Statistics only needs to return Averages, Standard Deviation and other statistics instead of the residue values. + diff --git a/pgd/docs/source/site_specific_information.rst b/pgd/docs/source/site_specific_information.rst new file mode 100644 index 0000000..b53d787 --- /dev/null +++ b/pgd/docs/source/site_specific_information.rst @@ -0,0 +1,55 @@ +==================== +Virtual environments +==================== + +The virtual environments for PGD on zeus are located under the PGD user's home directory. + + * pgd-staging: /home/pgd/.virtualenvs/pgd-staging/ + * pgd-prod: /var/lib/django/pgd-prod/pgd (will move to /home/pgd/.virtualenvs/pgd-prod/ ASAP) + +In addition to the traditional contents of a virtual environment as directed by the requirements file, the bin directory in the virtual environment also contains the binary:: + + dsspcmbi + +which is used by ProcessPDBTask.py to import data into the database. + +========================================= +Files generated during the update process +========================================= + +Text files pertaining to previous updates (selection files, reports, crosschecks, etc.) should be moved to the following directory after the update is complete:: + + /home/pgd/update-records + +These files should follow a naming format like this before moving:: + + 20131231-selection.txt + +Compressed SQL files generated by the update tool should be renamed in the same manner after the update is complete, but they are too large to be moved at this time. + +========================================== +Differences between staging and production +========================================== + +The staging directory has two directories which are not found in the production directory. These directories hold the files downloaded from the WWPDB and CMBI which are imported into the staging database. Their names are:: + + pdb + dssp + + +They may move, possibly to the home directory, at a later date. + +The production directory has a number of files which are not present in the staging directory and are also not in source control. + + * Historical copies of the staging and production databases for the pgd_core app. + * There are large compressed SQL files which represent the state of the staging and production databases for the pgd_core app in the production directory. These are used during the update process, and can be used for post-update analysis. Any files of this type older than one year can be deleted. + * lib64 + * It is not clear to me what this directory is for -- it may be part of the virtualenv. + * searchConvert.py + * This file was used to convert certain data structures from version 0.9.2 to 0.9.3 and can probably be deleted. + * static/pdf/2011_tronrud-shelxl.pdf + * This is a local copy of the paper which is cited as a source when referencing PGD in publications. There are five other papers in that directory which are in the repository, so this one should probably be added. + * update-from-staging.sh + * This script is executed to update the production database from the staging database. + +In addition to these differences, the settings.py for staging and production is different with regard to database configuration and version settings. diff --git a/pgd/docs/source/splicer.rst b/pgd/docs/source/splicer.rst new file mode 100644 index 0000000..9366948 --- /dev/null +++ b/pgd/docs/source/splicer.rst @@ -0,0 +1,52 @@ +******* +Splicer +******* + +Splicer is the tool used to import data from PDB files. + +Splicer is built as a Parallel Pydra Task. Pydra is a framework for parallel and distributed jobs with python. Using Pydra allows splicer run times to be reduced nearly linearly across nodes in the cluster. + +============== +Task Structure +============== + +Splicer is made up of several subtasks. It is organized using both `ContainerTask `_ (sequential subtasks) and `ParallelTask `_ to divide work up and parallelize it +It is organized like so: + * Splicer (!ContainerTask) + * Selector + * ProcessProteinTask(!ParallelTask) + * FTPUpdate + * Processor + * SegmentBuilder + +-------- +Selector +-------- + +Selector downloads lists of pdbs from a source, currently `Dunbracks PICSES `_, and processes the list into a python object. + +There are two selectors: + * DunbracksSelector - selects records from dunbracks culled lists + * PDBSelectSelector - selects records from pdbselect lists. This was replaced with dunbrack's selector + +Both selectors filter on and parse protein level properties from the list. + +---------- +FTP Update +---------- + +Synchronizes a local cache of PDBs with a remote ftp server. By default it will synchronize all files located on the remote directory, optionally it can be given a list of PDB codes to synchronize. + +FTP Update compares dates to the milisecond. This requires using the MODTIME (mdtm) FTP command. + +--------- +Processor +--------- + +The processor parses a pdb file and extracts properties from it. The current implementation uses *BioPython* library and DSSP. This component is `covered in depth `_. + +--------------- +Segment Builder +--------------- + +This is a defunct subtask that was used to build the *de-normalized segment table* diff --git a/pgd/docs/source/sql_aggregate_functions.rst b/pgd/docs/source/sql_aggregate_functions.rst new file mode 100644 index 0000000..5dce388 --- /dev/null +++ b/pgd/docs/source/sql_aggregate_functions.rst @@ -0,0 +1,62 @@ +************** +SQL Aggregates +************** + +SQL aggregates are functions that run on the server. They can perform statistics such as **Count, Average, Standard Deviation, Min,** and **Max**. Aggregate functions can also be paired with **GROUP BY** to calculate statistics for different groupings of data. + +Django supports aggregate functions as of 1.1. Read more about it here: `Django Aggregates `_ :: + + SELECT COUNT(L1), MIN(L1), MAX(L1), AVG(L1), STDDEV(L1) FROM pgd_core_residue GROUP BY aa; + +Aggregate functions increase the speed of calculations because they are run on the data in place. Transferring data between the database server and application server requires significant overhead. + +============================== +Statistics for Dihedral Angles +============================== + +.. image:: directional_average.png + +Dihedral angles require special function for average and standard deviation. The special function takes into account that the angles may wrap around from 180 to -180. Both of these functions work like any other aggregate functions. They have also been wrapped in a custom Django Aggregate function to work with django querysets. + +======= +Average +======= +:: + IF(DEGREES(ATAN2( + -AVG(SIN(RADIANS(ome))) + ,-AVG(COS(RADIANS(ome)))) + ) < 0 + ,DEGREES(ATAN2( + -AVG(SIN(RADIANS(ome))) + ,-AVG(COS(RADIANS(ome)))) + ) + 180 + ,DEGREES(ATAN2( + -AVG(SIN(RADIANS(ome))) + ,-AVG(COS(RADIANS(ome)))) + ) - 180 + ) AS ome_avg + +This works by converting the value into vectors. It adjusts the angles by +180 or -180 depending on whether it is a positive or negative angle. This shifts the vectors into the same space so that they may be averaged. + +================== +Standard Deviation +================== +:: + SQRT( + IF (((ome+360)%360 - avgs.ome_avg) < 180 + ,SUM(POW((ome+360)%360-avgs.ome_avg, 2)) + ,SUM(POW(360-((ome+360)%360-avgs.ome_avg),2)) + )/(COUNT(ome)-1)) + AS OME_STDDEV + +This function is very similar to a normal standard deviation calculation. The only difference is that a dihedral angle can have two deviations, the short and long way around the circle. We always want to use the shortest distance. + +----------------- +Average Selection +----------------- + +This standard deviation aggregate requires that the average be passed in. There are only two ways to match a list of averages to groups, subqueries or case logic. Neither is an ideal solution but case logic is the lesser of two evils. :: + + CASE SS WHEN 'B' THEN 'foo' WHEN 'H' THEN 'bar' END + +This results in queries that are very long (text size), but execution time is fast enough. diff --git a/pgd/docs/source/sql_indexes.rst b/pgd/docs/source/sql_indexes.rst new file mode 100644 index 0000000..016987a --- /dev/null +++ b/pgd/docs/source/sql_indexes.rst @@ -0,0 +1,63 @@ +************************* +Optimization: SQL Indexes +************************* + +Like any database PGD relies on SQL Indexes for improved performance. This is a description of indexes used, and some that didn't work. + +Please see the section on `Memory Table Indexes `_ for more information about the types of indexes used with memory tables. + +======= +Protein +======= + +----------------- +Primary Key Index +----------------- + +The primary key index is used when specific proteins have been selected by Code (primary key) + +---------------- +Resolution Index +---------------- + +The index on resolution is used in most cases. It filters large sets of proteins. The default query, with resolution <= 1.2 reduces the number of proteins from 16,000 to 2500. + +As the number of proteins nears the total number of proteins MySQL will switch to performing a full table scan. Even with indexes on other fields it does not appear to use them. + +-------------- +Failed Indexes +-------------- + +We also attempted to create indexes with resolution and other fields. No noticeable increase was detected, MySQL always opted for the individual Resolution Index. + +^^^^^^^^^^^^^^^^^^^^^^^^^ +Protein Joined to Residue +^^^^^^^^^^^^^^^^^^^^^^^^^ + +When joining a Residue with its Protein an index on Residue.protein_id is used + +-------------- +Failed Indexes +-------------- + +We attenmpted to add additional fields to the protein_id index. It was actually slower than the protein_id index alone. + +========================= +Residue Joined to Residue +========================= + +Residues are joined to Residues for the previous and next relationships using the Primary Key index on Residue. + +-------------------------------------------- +Note on Join Direction for Previous and Next +-------------------------------------------- + +Residues join from **residue_0.next** to **residue_1.id** :: + + SELECT * FROM pgd_core_residue r0 INNER JOIN pgd_core_residue r1 ON (r0.next = r1.id) + +instead of **residue_0.id** to **residue_1.prev** :: + + SELECT * FROM pgd_core_residue r0 INNER JOIN pgd_core_residue r1 ON (r0.next = r1.id) + +The latter appeared to be a faster query but is not possible with Django. The custom clause requires adding the where clause with `queryset.extra() `_. But django will also add the original clause. diff --git a/pgd/docs/source/technologies.rst b/pgd/docs/source/technologies.rst new file mode 100644 index 0000000..42fc600 --- /dev/null +++ b/pgd/docs/source/technologies.rst @@ -0,0 +1,37 @@ +********************************************** +Technologies Used By Protein Geometry Database +********************************************** + +====== +Django +====== + +Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Django provides some of the following features that are useful to PGD: + + * Object Relational Mapping (ORM) - maps python classes to SQL tables, including a comprehensive query engine. + * Form API for validation of user input + * Templating system for layout of presentation + + Read more at: djangoproject.com + +====== +jQuery +====== + +jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is used extensively in the front end to provide a "web 2.0" experience with dynamically updating pages. + +Read more at: jquery.com + +======= +Raphael +======= + +Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web. Raphael is cross browser supported and is used to render graphs within PGD. + +Read more at: raphaeljs.com + +===== +Mysql +===== + +Mysql is an opensource database diff --git a/pgd/docs/source/terminology.rst b/pgd/docs/source/terminology.rst new file mode 100644 index 0000000..e5dbe93 --- /dev/null +++ b/pgd/docs/source/terminology.rst @@ -0,0 +1,39 @@ +================= +Terminology (WIP) +================= + +Protein * + +Sidechain * + +Residue + + * Each residue has many attributes + * Conformation Angles (Angles of Rotation) + * phi, psi, ome (omega), omep (omega prime) + * Bond Angles + * a1, a2, a3, a4, a5, a6, a7 + * Bond Lengths + * L1, L2, L3, L4, L5 + * Secondary Structure + * ss + * Chi Angles, properties vary per Residue + * chi1, chi2, chi3, chi4, chi5 + * bm + * bs + * bg + * h_bond_energy + * zeta + * terminal_flag + * xpr + +Plots + + * In the plot each hit is sorted into a box based on the values of its attributes listed as "X Axis" and "Y Axis". Any attribute can be chosen as X or Y. The "Plotted Attribute" of all the hits that end up in the same box are averaged (and the standard deviation is calculated). If no hits fall in a box there is no meaningful average so the box is left blank. The occupied boxes are colored based on the average value in that box. + + * The exception is the default, when the "Plotted Attribute" is "Observations". Then the color is simply calculated from the number of hits that end up in the box, with zero being black. Regardless of the "Plotted Attribute" the same boxes should always be black. + +Search + + * Returns a set of residues + * When the "Plotted Attribute" is "Observations" the color is simply calculated from the number of hits that end up in the box, with zero being black. Regardless of the "Plotted Attribute" the same boxes should always be black. diff --git a/pgd/docs/source/updating_protein_database.rst b/pgd/docs/source/updating_protein_database.rst new file mode 100644 index 0000000..260dfa4 --- /dev/null +++ b/pgd/docs/source/updating_protein_database.rst @@ -0,0 +1,59 @@ +The PGD database is updated approximately four times per year. The following process should be followed to update the database. + +One week before update: generate new selection file and download majority of updates. + + *From the pgd-staging Django site:: + + 1 > python manage.py fetch --report=date-report.txt --selection=date-selection.txt* + +One day before update: reset staging database and load new selection file. + + *From the pgd-staging Django site:: + + 1 > python manage.py shell + + + 1 > from pgd_core.models import Protein + 2 > for p in Protein.objects.all(): + 3 > p.delete() + + 1 > ./pgd_splicer/ProcessPDBTask.py --pipein < date-selection.txt* + +On the update day: do the update! + + *From the pgd-staging Django site:: + + 1 > ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt + 2 > ./pgd_splicer/ftpupdate.py --pipein < selection.txt + 3 > ./pgd_splicer/ProcessPDBTask.py --pipein < selection.txt* + +That last command should be run within script so the output can be examined for particular failure modes once the command is complete. +Error messages and recommended actions: + + * "CRC check failed", "local variable 'i' referenced before assignment", "KeyError": collect all codes with these message, delete the files associated with these codes, retrieve them again from the server, and attempt to import the files again. Example command lines for codes 1B12 and 4AMW:: + + 1 > (cd ./pdb && for code in 1b12 4amw; do rm pdb$code.ent.gz; done) + 2 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ftpupdate.py --pipein + 3 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ProcessPDBTask.py --pipein + +If the errors persist, document offending codes in bug reports as appropriate and copy the retrieved files aside for testing and comparison. * "Structure/DSSP mismatch", "No chains were parsed!": document offending codes in update post to PGD mailing list. + +Cross-check database against selection file. + + *From the pgd-staging Django site:: + + 1 > python manage.py crosscheck --selection=selection.txt* + +The staging site is now ready for customer preview. + +Promote database from staging to production. + + *From the pgd-prod Django site:: + + 1 > ./update-from-staging.sh* + +Rename the selection file for archival purposes. :: + + 1 > mv selection.txt 201310-selection.txt + +**On production, in settings.py, update the DATA_VERSION to the date used for the selection file.** From 0d5d9dc1dabdab8c128273e5f9bdc775c3535505 Mon Sep 17 00:00:00 2001 From: MaraJade Date: Fri, 27 Jun 2014 09:21:21 -0700 Subject: [PATCH 13/28] Added liberation fonts to package requirements refs #15999 Liberation fonts is required for pgd to be installed, but was not in the package requirements --- pgd/docs/source/installation.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/pgd/docs/source/installation.rst b/pgd/docs/source/installation.rst index 12125b5..29f50bf 100644 --- a/pgd/docs/source/installation.rst +++ b/pgd/docs/source/installation.rst @@ -31,6 +31,7 @@ The following packages are required to run Protein Geometry Database. Either ins `_ * `Py2Cairo `_ See instructions below for installing the correct Py2Cairo * `Selenium `_ for running browser-based tests + * liberation fonts - yum -y install git python-devel mysql-devel libffi-devel liberation-sans-fonts The process for installing "py2cairo" was recently reported to be: From e91a651c3ded516f1f92d24560fd84962885ebb4 Mon Sep 17 00:00:00 2001 From: MaraJade Date: Mon, 7 Jul 2014 10:59:23 -0700 Subject: [PATCH 14/28] Fixed pbd missing errors refs #16353 Added an if statement checking whether or not the pdb directory exists, and creating it if it doesn't --- pgd/pgd_splicer/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pgd/pgd_splicer/tests.py b/pgd/pgd_splicer/tests.py index abc62b2..8eb7f68 100644 --- a/pgd/pgd_splicer/tests.py +++ b/pgd/pgd_splicer/tests.py @@ -189,6 +189,9 @@ def __enter__(self): self.old_urlopen = urllib.urlopen urllib.urlopen = MonkeyPatch.urlopen + if not os.path.exists(ftp_update_settings.PDB_LOCAL_DIR): + os.makedirs(ftp_update_settings.PDB_LOCAL_DIR) + # Replace all PDB entries with our test entries. # JMT: consider making a separate 'testpdb' directory? codes = ['1mwq', '1mww', '1twf', '3cgm', '3cgx', '3cgz'] From caa2c24e03e9db8f70625b894815c499a7c398a6 Mon Sep 17 00:00:00 2001 From: MaraJade Date: Mon, 7 Jul 2014 10:59:23 -0700 Subject: [PATCH 15/28] Fixed pbd missing errors refs #16353 Added an if statement checking whether or not the pdb directory exists, and creating it if it doesn't --- pgd/pgd_splicer/tests.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pgd/pgd_splicer/tests.py b/pgd/pgd_splicer/tests.py index abc62b2..8eb7f68 100644 --- a/pgd/pgd_splicer/tests.py +++ b/pgd/pgd_splicer/tests.py @@ -189,6 +189,9 @@ def __enter__(self): self.old_urlopen = urllib.urlopen urllib.urlopen = MonkeyPatch.urlopen + if not os.path.exists(ftp_update_settings.PDB_LOCAL_DIR): + os.makedirs(ftp_update_settings.PDB_LOCAL_DIR) + # Replace all PDB entries with our test entries. # JMT: consider making a separate 'testpdb' directory? codes = ['1mwq', '1mww', '1twf', '3cgm', '3cgx', '3cgz'] From c112ff1296531ea92e41921a6d76859237b6eb04 Mon Sep 17 00:00:00 2001 From: MaraJade Date: Wed, 9 Jul 2014 15:17:46 -0700 Subject: [PATCH 16/28] Modified files to get rid of errors refs #15999 Modified the files to get rid of the errors when building. Usually just indentation errors. Also modified headings, so that only the major ones are in the index --- pgd/docs/source/browse.rst | 6 +-- pgd/docs/source/code_modules.rst | 16 +++---- pgd/docs/source/data_dump.rst | 10 ++-- pgd/docs/source/denormalized_search_table.rst | 4 +- pgd/docs/source/developing_splicer.rst | 18 +++---- pgd/docs/source/development_workflow.rst | 28 ++++++----- pgd/docs/source/hotfix_workflow.rst | 32 +++++++------ pgd/docs/source/importing_data.rst | 24 ++++++---- pgd/docs/source/in_memory_tables.rst | 16 +++---- pgd/docs/source/index.rst | 2 +- pgd/docs/source/installation.rst | 48 ++++++++++--------- pgd/docs/source/processor_subtask.rst | 1 + pgd/docs/source/protein_data_models.rst | 14 +++--- pgd/docs/source/ramachandran_plot.rst | 12 ++--- pgd/docs/source/release_workflow.rst | 24 ++++++---- pgd/docs/source/running_splicer.rst | 12 ++--- .../running_splicer_from_command_line.rst | 16 +++---- pgd/docs/source/search_statistics.rst | 24 +++++----- pgd/docs/source/search_workflow.rst | 38 +++++++-------- pgd/docs/source/site_specific_information.rst | 16 ++++--- pgd/docs/source/splicer.rst | 23 +++++---- pgd/docs/source/sql_aggregate_functions.rst | 25 ++++++---- pgd/docs/source/sql_indexes.rst | 28 +++++------ pgd/docs/source/technologies.rst | 16 +++---- pgd/docs/source/terminology.rst | 4 +- pgd/docs/source/updating_protein_database.rst | 30 +++++++----- 26 files changed, 263 insertions(+), 224 deletions(-) diff --git a/pgd/docs/source/browse.rst b/pgd/docs/source/browse.rst index cc680b5..3aa7c9f 100644 --- a/pgd/docs/source/browse.rst +++ b/pgd/docs/source/browse.rst @@ -4,14 +4,14 @@ Browse Browse displays all residues for each segment in a result set. Results a -============== +-------------- Selecting Data -============== +-------------- Queries using Django's ORM focus on a single object. Accessing related fields such as **Residue.prev** or **Residue.next** result in a second query to resolve those objects. This means to display a single segment of length 5 you must do 4 additional queries. To display 25 segments per page would require 125 additional queries. The solution is to calculate the list of residues for each segment and select -them by range. For example, to retrieve a segment of length 3 where i=3 select all residues from i-1 to i+1 (2 through 4). :: +them by range. For example, to retrieve a segment of length 3 where i-3 select all residues from i-1 to i+1 (2 through 4). :: Select * from pgd_core_residue where chainIndex between 2 and 4 diff --git a/pgd/docs/source/code_modules.rst b/pgd/docs/source/code_modules.rst index ea712a9..72e73b6 100644 --- a/pgd/docs/source/code_modules.rst +++ b/pgd/docs/source/code_modules.rst @@ -1,25 +1,25 @@ -============ +************ Code Modules -============ +************ The Protein Geometry Database makes use of Django "apps". Apps are synonymous with a module or plugin. They are module bits of an application that are, for the most part, portable between other apps. PGD is divided into three apps to allow code to be portable. --------- +^^^^^^^^ PGD Core --------- +^^^^^^^^ PGD Core defines the core data structures. There is no functionality contained within this app. It is intended to contain only the database so that it can be reused in other applications ----------- +^^^^^^^^^^ PGD Search ----------- +^^^^^^^^^^ PGD Search contains logic for searching and displaying data from PGD Core. This includes search logic, models, and the web front end. ------------ +^^^^^^^^^^^ PGD Splicer ------------ +^^^^^^^^^^^ PGD Splicer contains all of the code required for importing data from PDB files into objects defined within PGD Core diff --git a/pgd/docs/source/data_dump.rst b/pgd/docs/source/data_dump.rst index a746a46..3cc7484 100644 --- a/pgd/docs/source/data_dump.rst +++ b/pgd/docs/source/data_dump.rst @@ -4,21 +4,21 @@ Data Dump Data dump is a dump of all fields of all segments returned by a search. -============== +-------------- Selecting Data -============== +-------------- Queries using Django's ORM focus on a single object. Accessing related fields such as **Residue.prev** or **Residue.next** result in a second query to resolve those objects. This means to display a single segment of length 5 you must do 4 additional queries. -The solution is to calculate the list of residues for each segment and select them by range. For example, to retrieve a segment of length 3 where i=3 select all residues from i-1 to i+1 (2 through 4). :: +The solution is to calculate the list of residues for each segment and select them by range. For example, to retrieve a segment of length 3 where i-3 select all residues from i-1 to i+1 (2 through 4). :: Select * from pgd_core_residue where chainIndex between 2 and 4 This allows all residues in a segment to be retrieved in a single query. This reduces the number of queries to the number of residues in the result set. This may still be a large number of queries but they may be run in parallel and the buffered nature of datadump spreads them out. -================= +----------------- Buffered Response -================= +----------------- Datadump uses an iterable class which uses threads to buffer data. The buffering threads use a paginator to split a result set into pieces. This allows downloading to start almost immediately after the button is clicked, rather than waiting for the entire dump to be written to memory. diff --git a/pgd/docs/source/denormalized_search_table.rst b/pgd/docs/source/denormalized_search_table.rst index 475254a..a30f9fe 100644 --- a/pgd/docs/source/denormalized_search_table.rst +++ b/pgd/docs/source/denormalized_search_table.rst @@ -6,8 +6,8 @@ Searching segments of residues requires joining the residue table on itself, num This optimization greatly out-performed joins with less than 20,000 results. However, when the result set increased to 300,000 or more results it was twice as slow. The reason was that the table was too large to perform an entire table scan. It only was sped up when there was an index touching every field a search had a clause for. It was not feasible to build an index touch all 200+ fields to allow quick searching. -========== +---------- Table Size -========== +---------- The table size was not completely unmanageable, it was only 7 gigabytes. This was fine for on-disk, but would not scale well in memory. diff --git a/pgd/docs/source/developing_splicer.rst b/pgd/docs/source/developing_splicer.rst index ef0be29..6f3ed33 100644 --- a/pgd/docs/source/developing_splicer.rst +++ b/pgd/docs/source/developing_splicer.rst @@ -4,9 +4,9 @@ Developing Splicer These are some special instructions for developing splicer components. -=============== +--------------- Django Settings -=============== +--------------- Django and its ORM can be used outside of its webserver. The only requirements are * the directory containing **settings.py** is on the sys.path @@ -18,18 +18,18 @@ Splicer components should all do this automatically using python code to add the #python magic to add the current directory to the pythonpath sys.path.append(os.getcwd()) - # ========================================================== + # ---------------------------------------------------------- # Setup django environment - # ========================================================== + # ---------------------------------------------------------- if not os.environ.has_key('DJANGO_SETTINGS_MODULE'): - os.environ['DJANGO_SETTINGS_MODULE'] = 'settings - # ========================================================== + os.environ['DJANGO_SETTINGS_MODULE'] - 'settings + # ---------------------------------------------------------- # Done setting up django environment - # ========================================================== + # ---------------------------------------------------------- -======================================== +---------------------------------------- Running Components from the command line -======================================== +---------------------------------------- Splicer Subtasks all contain **main** code that starts the tasks using arguments passed in. All should give you a list of required arguments if you do not pass them in. The main intention of this is to allow easier debugging on tasks. diff --git a/pgd/docs/source/development_workflow.rst b/pgd/docs/source/development_workflow.rst index 32e8a42..2b1f62d 100644 --- a/pgd/docs/source/development_workflow.rst +++ b/pgd/docs/source/development_workflow.rst @@ -1,37 +1,41 @@ -=================================================================== +******************** +Development workflow +******************** + +------------------------------------------------------------------- Create an issue in the issue tracker if one does not already exist. -=================================================================== +------------------------------------------------------------------- All modifications to the source code should be associated with an issue for tracking purposes. -================================================================== +------------------------------------------------------------------ Create a branch from the develop branch based on the issue number. -================================================================== +------------------------------------------------------------------ The branch should be named based on the issue type and number. For now, all issues are considered bugs for naming purposes. The branch for issue #14109 (a feature) should be named 'bug/14109'. At this time, the issue should be updated with the status 'In Progress'. -===================================================================== +--------------------------------------------------------------------- Write the code, including tests and release notes entry if necessary. -===================================================================== +--------------------------------------------------------------------- If the branch is fixing a bug, then a test should be written first if at all possible to confirm the bug exists as well as confirm that the bug fix works. If the branch makes any user-visible changes, then the release notes (news.html) should be updated to reflect the changes. If you are the first developer to post release notes for the latest development version, add an appropriate header above the existing release notes following the example format -- the release engineer will clean it up if necessary when making the next release. -================================= +--------------------------------- Commit code and update the issue. -================================= +--------------------------------- If any billable work is done on an issue, then code should be committed, and the issue should be updated with the number of billable hours spent on the task and a summary of the work that was done. Commits and updates should happen whenever major subtasks are completed and at close of business. -==================== +-------------------- Request code review. -==================== +-------------------- When the code is complete, the tests if any all pass, and the release notes entry has been added if necessary, mark the issue 'Needs Review' and assign it to another team member for review. That individual will review the code, the tests, the entry, and any other associated changes for accuracy and consistency. If the branch is acceptable, the reviewer will mark the issue 'Resolved'. If the branch is unacceptable, the reviewer will mark the issue 'Needs Work'. In both cases, the reviewer will update the issue with relevant information and reassign the issue back to the original developer. -=========================================== +------------------------------------------- Merge changes back into the develop branch. -=========================================== +------------------------------------------- Once the branch passes review, it should be merged back into develop using 'git merge --no-ff' and then develop should be pushed back into the origin. Once this is complete, the devloper should update the issue to that effect. diff --git a/pgd/docs/source/hotfix_workflow.rst b/pgd/docs/source/hotfix_workflow.rst index f3d9c2c..dd6243d 100644 --- a/pgd/docs/source/hotfix_workflow.rst +++ b/pgd/docs/source/hotfix_workflow.rst @@ -1,44 +1,48 @@ -============================================================ +*************** +Hotfix Workflow +*************** + +------------------------------------------------------------ One or more show-stopping bugs are detected between releases -============================================================ +------------------------------------------------------------ An example of a show-stopping bug would be something like #14613 where an unexpected side-effect of a bugfix caused incomplete results to be returned on unrelated searches. -============================================ +-------------------------------------------- Create a hotfix branch off the master branch -============================================ +-------------------------------------------- The hotfix branch name will take the form hotfix/x.y.z+1 If the current release is 1.2 the hotfix branch is then hotfix/1.2.1 If the current release is 3.7.2 the hotfix branch is then hotfix/3.7.3 -========================================= +----------------------------------------- Create bug branches off the hotfix branch -========================================= +----------------------------------------- For each bug that must be fixed, an issue is created and a bug branch named after that issue like all other bugs is created from the hotfix branch instead of the develop branch like all other bugs. -============================================================= +------------------------------------------------------------- Merge resolved bugs back to hotfix branch and test on staging -============================================================= +------------------------------------------------------------- As each individual bug is resolved, its branch is merged back into the hotfix branch. The hotfix branch can then be updated on the staging server for testing. -============================================================================= +----------------------------------------------------------------------------- When all bugs are resolved and merged, merge hotfix branch into master branch -============================================================================= +----------------------------------------------------------------------------- Do not forget to increment the version, create a new tag, and update the news page with all bug fixes! -============================================ +-------------------------------------------- Pull master on production and restart Apache -============================================ +-------------------------------------------- This should make the new version accessible to the user community. -===================================== +------------------------------------- Merge hotfix branch back into develop -===================================== +------------------------------------- Once production is back up and running, take the time to merge the hotfix branch back into develop. diff --git a/pgd/docs/source/importing_data.rst b/pgd/docs/source/importing_data.rst index d3be88c..3aefaca 100644 --- a/pgd/docs/source/importing_data.rst +++ b/pgd/docs/source/importing_data.rst @@ -1,6 +1,10 @@ -===================================== +************** +Importing Data +************** + +------------------------------------- Running Splicer From The Command Line -===================================== +------------------------------------- Splicer can be run from the command line. It requires that several steps be run separately. @@ -22,9 +26,9 @@ Save the selection into a file:: 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt -------- +^^^^^^- Options -------- +^^^^^^- * --pipeout - will only output the data. This should be used if you would like to create output suitable for input into one of the other steps @@ -56,9 +60,9 @@ As before, a selection can be piped in:: Expect this to take a few days as well. ----------- +^^^^^^^^^^ Parameters ----------- +^^^^^^^^^^ Parameters are all required, and may be repeated for multiple proteins. @@ -66,15 +70,15 @@ Parameters are all required, and may be repeated for multiple proteins. * **chains** - list of chains to import, should be a string of chain ids. (ie. ABCDEF). The string should not have quotes around it. * **threshold, resolution, rfactor, rfree** - the value for these fields. These properties are retrieved from the selection script so they are included as input for processing the protein. -------- +^^^^^^^ Options -------- +^^^^^^^ * --pipein - input will be read from a pipe instead of arguments. proteins in the list should be separated by newlines. -======= +------- Example -======= +------- Some examples. Intermediate output is saved to a text file so that it can be examined later. diff --git a/pgd/docs/source/in_memory_tables.rst b/pgd/docs/source/in_memory_tables.rst index 697199c..205dd9a 100644 --- a/pgd/docs/source/in_memory_tables.rst +++ b/pgd/docs/source/in_memory_tables.rst @@ -4,17 +4,17 @@ Optimization: In Memory Tables Loading PGD tables in memory, if indexed properly, will greatly outperform on-disk tables. It is a solution dependent on a capable enough server. -====================== +---------------------- Indexing Memory Tables -====================== +---------------------- Memory tables will only outperform a properly indexed on-disk table if it is also indexed. Above a certain threshold a full table scan of a memory table is still slower than a binary search of an index, even when it is on disk. Memory tables support indexing, but mysql does not correctly index when using btree. Btree is the default for on-disk tables, and is generally faster than a Hash index. however, mysql will generate an empty index when btree is used with memory tables. This means that any table joined with a memory table must also be in memory. -========================== +-------------------------- Parallelization of Queries -========================== +-------------------------- Memory addresses can be read by multiple threads simultaneously, unlike disks which require seeking back and forth. Without seek times to slow down simultaneous reads, multiple queries can be run on an in memory table at the same time. This can reduce the time required for a query to the longest in the set of queries @@ -22,9 +22,9 @@ Memory addresses can be read by multiple threads simultaneously, unlike disks wh Note that this becomes a limitation of CPU cores and the number of concurrent threads the server is capable of handling. -======================================= +--------------------------------------- Startup and Django Configuration Issues -======================================= +--------------------------------------- Memory tables do not persist through mysql restarts. They must be recreated and indexed every time the server starts. This needs to be automated so that when the server first starts it is able to check and create the tables if needed. @@ -33,9 +33,9 @@ Django must also be told what this special table is. The choices are: * rename the table during the creation process. It might be impossible to determine the state of the tables though. * change the name of the table in the django configuration to match the in memory table. this can only be done prior to django loading, the django orm cannot be reinitialized -=============== +--------------- Growth Concerns -=============== +--------------- Growth of the database is a major concern when dealing with large memory tables. Ram is cheap, but not as cheap as disk space. The current PGD database requries about 1.5 gigabytes (1.1 for Residues) of ram to load the Protein and Residue table in memory. Two factors will increase growth: diff --git a/pgd/docs/source/index.rst b/pgd/docs/source/index.rst index d8f2608..eb1b1a8 100644 --- a/pgd/docs/source/index.rst +++ b/pgd/docs/source/index.rst @@ -9,7 +9,7 @@ Welcome to Protein Geometry Database's documentation! Contents: .. toctree:: - :maxdepth: 3 + :maxdepth: 1 installation importing_data diff --git a/pgd/docs/source/installation.rst b/pgd/docs/source/installation.rst index 29f50bf..ad0c585 100644 --- a/pgd/docs/source/installation.rst +++ b/pgd/docs/source/installation.rst @@ -1,6 +1,10 @@ -******************************* +************ +Installation +************ + +=============================== Development Branch Installation -******************************* +=============================== Dependencies:: libmysqlclient-dev @@ -8,9 +12,9 @@ Dependencies:: In a virtualenv:: pip install -r pgd/requirements.txt -************************** +========================== Master Branch Installation -************************** +========================== This is a manual installation guide for the Protein Geometry Database ^^^^^^^^^^^^^^^^^^^^ @@ -25,10 +29,8 @@ The following packages are required to run Protein Geometry Database. Either ins * `MySQL-python `_ * `numpy `_ * `biopython 1.57 `_ - * `Python Django 1.3.x - `_ - * `Python Django-registration ==0.7 - `_ + * `Python Django 1.3.x `_ + * `Python Django-registration --0.7 `_ * `Py2Cairo `_ See instructions below for installing the correct Py2Cairo * `Selenium `_ for running browser-based tests * liberation fonts - yum -y install git python-devel mysql-devel libffi-devel liberation-sans-fonts @@ -83,16 +85,16 @@ Configuration ./manage.py runserver -============== +-------------- Importing Data -============== +-------------- See the `importing data `_ section for instructions on how to import data. -================================================ +------------------------------------------------ Additional configuration for production servers: -================================================ +------------------------------------------------ Deploying a production server requires additional setup steps. @@ -100,16 +102,16 @@ Deploying a production server requires additional setup steps. 2. Ensure the server has the ability to send emails or you have access to an SMTP server. Set EMAIL_HOST, EMAIL_PORT, and DEFAULT_FROM_EMAIL in settings.py. For more complicated outgoing mail setups, please refer to the django email documentation. 3. Follow the django guide to deploy with apache. Here is an example mod_wsgi file:: - 1 import os - 2 import sys - 3 - 4 path = '/var/lib/django/pgd' - 5 if path not in sys.path: - 6 sys.path.append(path) - 7 - 8 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' - 9 - 10 import django.core.handlers.wsgi - 11 application = django.core.handlers.wsgi.WSGIHandler() + 1 import os + 2 import sys + 3 + 4 path = '/var/lib/django/pgd' + 5 if path not in sys.path: + 6 sys.path.append(path) + 7 + 8 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings' + 9 + 10 import django.core.handlers.wsgi + 11 application = django.core.handlers.wsgi.WSGIHandler() 4. If New Relic support is required, modify the WSGI file according to the New Relic documentation. On zeus, the New Relic configuration file is /etc/newrelic.ini. diff --git a/pgd/docs/source/processor_subtask.rst b/pgd/docs/source/processor_subtask.rst index 6975137..f495b62 100644 --- a/pgd/docs/source/processor_subtask.rst +++ b/pgd/docs/source/processor_subtask.rst @@ -81,6 +81,7 @@ Example B-factor: Bm, Bs, Bg ============================ :: + """ Other B Averages Bm - Average of bfactors in main chain. diff --git a/pgd/docs/source/protein_data_models.rst b/pgd/docs/source/protein_data_models.rst index 0bc1696..5dad85f 100644 --- a/pgd/docs/source/protein_data_models.rst +++ b/pgd/docs/source/protein_data_models.rst @@ -2,7 +2,7 @@ Protein Data Models ******************* -.. image:: protein_model.png +.. .. image:: protein_model.png Protein data model is composed of three classes: **Protein, Chain,** and **Residue**. The models are designed to represent the protein in the most compact way. With proper SQL indexes this is also the most efficient method of storing the data for search queries when the database contains greater than 2 million residues. @@ -15,9 +15,9 @@ For more information on Django models: * `Django Query Reference `_ -======= +------- Protein -======= +------- Represents a Protein. @@ -29,18 +29,18 @@ Represents a Protein. * **residues** - related field collection, returns queryset of all residues related to this protein * **chains** - related field colleciton, returns queryset of all chain related to this protein -===== +----- Chain -===== +----- Chain id's are stored to allow importing of multiple chains from a single protein. The search interface does not currently include selection of chains. * **protein** (protein_id) Foreign Key relation to the protein this residue belongs to. May be retrieved as a Protein object or identifier * **residues** - related field collection, returns queryset of all residues related to this protein -======= +------- Residue -======= +------- Represents a Residue (Amino Acid) belonging to a protein diff --git a/pgd/docs/source/ramachandran_plot.rst b/pgd/docs/source/ramachandran_plot.rst index 46c0016..00747f3 100644 --- a/pgd/docs/source/ramachandran_plot.rst +++ b/pgd/docs/source/ramachandran_plot.rst @@ -6,9 +6,9 @@ PGD provides a pseudo ramachandran plot. Our plot uses square bins rather than a The Plot page uses django querysets, but makes use of [SQL Aggregates]. For ease in explaining how the data is processed this page will refer to the SQL generated by the querysets -============== +-------------- Data Selection -============== +-------------- The Ramachandran plot is generated using a specialized SQL query to group data points into bins. :: @@ -19,9 +19,9 @@ The Ramachandran plot is generated using a specialized SQL query to group data p Subtracting the minimum value from the coordinate shifts the start of the bins to the minimum value. The first bin will always be the same size as other bins. The last bin may be a different size if BIN_SIZE does not divide MAX-MIN evenly -====================== +---------------------- Statistics Calculation -====================== +---------------------- Once records are selected statistical calculations are performed depending on the input. By default the ramachandran plot z-axis displays observations, or the count of residues in that bin. :: @@ -31,9 +31,9 @@ Optionally the z-axis can also display the average of user selected attribute. : select AVG(a1) as avg, STDDEV(a1) as stddev, FLOOR((phi-PHI_MIN)/10) as X, FLOOR((psi-PSI_MIN)/10) as Y from pgd_core_residue GROUP BY X, Y -======== +-------- Coloring -======== +-------- Graphs are colored to represent values on a z-axis. diff --git a/pgd/docs/source/release_workflow.rst b/pgd/docs/source/release_workflow.rst index 8239d04..128a7a6 100644 --- a/pgd/docs/source/release_workflow.rst +++ b/pgd/docs/source/release_workflow.rst @@ -1,22 +1,28 @@ -======================================================================== +**************** +Release Workflow +**************** + +------------------------------------------------------------------------ Announce the upcoming release to the PGD list four weeks before release. +------------------------------------------------------------------------ + Include the following information: * version number to be released in x.y.z format * rough list of features and bugfixes expected to be included * schedule of events (feature freeze, release branch, etc.) -============================================================== +-------------------------------------------------------------- Impose a feature freeze on develop three weeks before release. -============================================================== +-------------------------------------------------------------- Features are no longer permitted to be merged into the develop branch, only bugs. Release engineer checks each resolved ticket to confirm that the branch was indeed merged into the develop branch. -================================================== +-------------------------------------------------- Start the release branch two weeks before release. -================================================== +-------------------------------------------------- Create a branch from develop named 'release/x.y.z' using the version number mentioned in the release announcement. @@ -24,17 +30,17 @@ Log into the dev site and check out the release branch there. Bugfixes can only be made from and returned to this branch at this time. -================================================== +-------------------------------------------------- Freeze the release branch one week before release. -================================================== +-------------------------------------------------- Log into the staging site and check out the release branch there. Only emergency fixes allowed at this point! -============================================================================= +----------------------------------------------------------------------------- Release the software, close tickets and unfreeze develop on the release date. -============================================================================= +----------------------------------------------------------------------------- Merge the release branch back into master and develop branches. diff --git a/pgd/docs/source/running_splicer.rst b/pgd/docs/source/running_splicer.rst index cf0429a..b5bf840 100644 --- a/pgd/docs/source/running_splicer.rst +++ b/pgd/docs/source/running_splicer.rst @@ -9,9 +9,9 @@ Splicer can be `deployed `_ and `run `_ -=============== +--------------- Slow FTP Issues -=============== +--------------- The FTP server for PDB files is a very slow, rate limited, server located in the UK. PDB files are currently 1.8 gigabytes total for 16,000 proteins in PGD. It can take a long time to download this much data from the FTP server. This is handled in two ways: @@ -35,17 +35,17 @@ Pydra can't guarantee that future runs of Splicer will process the same set of p Note that this only matters when using -========================== +-------------------------- Workunit Thrashing Problem -========================== +-------------------------- There is an outstanding `bug in pydra that causes the node to crash when workunits complete too quickly `_. Splicer includes an option to batch process proteins to ensure that this does not happen. Eventually `batching workunits `_ will an automatic feature of Pydra When running repeat runs of Pydra it is important to increase the workunit size to at least 500-1000. Because the date checks are very fast it will cycle through the existing proteins very quickly. -================= +----------------- Debugging Splicer -================= +----------------- Pydra `logs `_ most things that happen within it. A full `task history `_ can be viewed by clicking the history icon found on the pydra `tasks page `_. Clicking on a task instance gives you more details about the task including which workunits were successful and what their arguments were. diff --git a/pgd/docs/source/running_splicer_from_command_line.rst b/pgd/docs/source/running_splicer_from_command_line.rst index b24317f..79ee356 100644 --- a/pgd/docs/source/running_splicer_from_command_line.rst +++ b/pgd/docs/source/running_splicer_from_command_line.rst @@ -22,9 +22,9 @@ Save the selection into a file:: 1 ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt -======= +------- Options -======= +------- * --pipeout - will only output the data. This should be used if you would like to create output suitable for input into one of the other steps @@ -56,9 +56,9 @@ As before, a selection can be piped in:: Expect this to take a few days as well. -========== +---------- Parameters -========== +---------- Parameters are all required, and may be repeated for multiple proteins. @@ -66,15 +66,15 @@ Parameters are all required, and may be repeated for multiple proteins. * chains - list of chains to import, should be a string of chain ids. (ie. ABCDEF). The string should not have quotes around it. * threshold, resolution, rfactor, rfree - the value for these fields. These properties are retrieved from the selection script so they are included as input for processing the protein. -======= +------- Options -======= +------- * --pipein - input will be read from a pipe instead of arguments. proteins in the list should be separated by newlines. -======= +------- Example -======= +------- Some examples. Intermediate output is saved to a text file so that it can be examined later. diff --git a/pgd/docs/source/search_statistics.rst b/pgd/docs/source/search_statistics.rst index b6f8dc9..bad06c9 100644 --- a/pgd/docs/source/search_statistics.rst +++ b/pgd/docs/source/search_statistics.rst @@ -4,15 +4,15 @@ Search Statistics The statistics page provides statistics of properties across all results for a specific residue index. Results are grouped by Amino Acid Type, and or Secondary Structure Type. -======= +------- Queries -======= +------- The statistics page makes use of aggregate functions to calculate values. Because of the different types of grouping and statistics, it requires several queries to retrieve all of the statistics. ------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Secondary Structure Counts and Amino Acid Totals ------------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This query produces three values: @@ -26,31 +26,31 @@ The SQL required is :: The **WITH ROLLUP** clause instructs mysql to include totals for the fields. This produces the total per amino acid type, and total count of all residues. WITH ROLLUP only affects the first field in the GROUP BY class so totals per Secondary Structure Type are not produced by this query. --------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- Secondary Structure Total Counts --------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-- Count of residues per Secondary Structure Type. :: Select count(*) as count, ss_type from pgd_core_residue GROUP BY ss; ----------------- +^^^^^^^^^^^^^^^^ Field Statistics ----------------- +^^^^^^^^^^^^^^^^ Min, Max, Average, and Standard Deviation are calculated for every residue, per Amino Acid Type, include totals. :: SELECT MIN(a1), MAX(a1), AVG(a1), STDDEV(a1) from pgd_core_residue GROUP_BY aa WITH ROLLUP; --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ Dihedral Angles Statistics --------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^ Dihedral angles (ome, zeta, phi, psi, etc.) require the use of `Special Statistics for Dihedral Angles `_. Average is calculated in the field statistics query, but standard deviation requires a second query. -============ +------------ Optimization -============ +------------ Statistics uses the following optimizations: diff --git a/pgd/docs/source/search_workflow.rst b/pgd/docs/source/search_workflow.rst index 58bfcdc..d5a7d12 100644 --- a/pgd/docs/source/search_workflow.rst +++ b/pgd/docs/source/search_workflow.rst @@ -4,44 +4,44 @@ Search Workflow PGD Search workflow is designed to take advantage of all of the features provided by Django: Models, Forms, and QuerySets. Search Models and Forms are interchangeable via methods provided by PGD. -====== +------ Models -====== +------ -.. image:: search_models.png +.. .. image:: search_models.png The Search class closely mimics a protein segment. It is composed of a three classes: Search, SearchResidue, and SearchCode. By creating Search as django models they can be stored and retrieved from the database. ------- +^^^^^^ Search ------- +^^^^^^ Search model contains all fields present in a protein. ----------- +^^^^^^^^^^ SearchCode ----------- +^^^^^^^^^^ SearchCode is a list of protein IDs to include in the search -------------- +^^^^^^^^^^^^^ SearchResidue -------------- +^^^^^^^^^^^^^ SearchResidue mimics a Residue object. It contains all fields that a * Fields are all strings and can use the PGD Query Syntax * Type fields such as AA_Type and SS_Type are stored as an integer with choices encoded in binary to conserve space. -===== +----- Forms -===== +----- SearchForms mimic Search Models exactly. -=========== +----------- Conversions -=========== +----------- Search and SearchForm are interchangeable. @@ -52,15 +52,15 @@ Search can generate Django QuerySets Use **search.queryset()** -======== +-------- Workflow -======== +-------- -.. image:: query_lifecycle.png +.. .. image:: query_lifecycle.png When submitting the search form, if the search is successful a **Search** object will be created and stored in the user's session. The **Search** can be retrieved and converted back into a **SearchForm** to edit the search. -.. image:: query_customization.png +.. .. image:: query_customization.png This **Search** object becomes the basis for rendering all other pages. PGD is taking advantage of two features of querysets: @@ -69,9 +69,9 @@ This **Search** object becomes the basis for rendering all other pages. PGD is t These features allow PGD apply view specific logic without recreating the **base query** for every **view**. The **base query** contains the set of records to operate on. The **view** applies its specific logic and calculations. --------- +^^^^^^^^ Examples --------- +^^^^^^^^ * Plot page only requires 3 properties to perform its calculation * Statistics only needs to return Averages, Standard Deviation and other statistics instead of the residue values. diff --git a/pgd/docs/source/site_specific_information.rst b/pgd/docs/source/site_specific_information.rst index b53d787..71ebcbd 100644 --- a/pgd/docs/source/site_specific_information.rst +++ b/pgd/docs/source/site_specific_information.rst @@ -1,6 +1,10 @@ -==================== +************************* +Site Specific Information +************************* + +-------------------- Virtual environments -==================== +-------------------- The virtual environments for PGD on zeus are located under the PGD user's home directory. @@ -13,9 +17,9 @@ In addition to the traditional contents of a virtual environment as directed by which is used by ProcessPDBTask.py to import data into the database. -========================================= +----------------------------------------- Files generated during the update process -========================================= +----------------------------------------- Text files pertaining to previous updates (selection files, reports, crosschecks, etc.) should be moved to the following directory after the update is complete:: @@ -27,9 +31,9 @@ These files should follow a naming format like this before moving:: Compressed SQL files generated by the update tool should be renamed in the same manner after the update is complete, but they are too large to be moved at this time. -========================================== +------------------------------------------ Differences between staging and production -========================================== +------------------------------------------ The staging directory has two directories which are not found in the production directory. These directories hold the files downloaded from the WWPDB and CMBI which are imported into the staging database. Their names are:: diff --git a/pgd/docs/source/splicer.rst b/pgd/docs/source/splicer.rst index 9366948..e072cd8 100644 --- a/pgd/docs/source/splicer.rst +++ b/pgd/docs/source/splicer.rst @@ -6,22 +6,25 @@ Splicer is the tool used to import data from PDB files. Splicer is built as a Parallel Pydra Task. Pydra is a framework for parallel and distributed jobs with python. Using Pydra allows splicer run times to be reduced nearly linearly across nodes in the cluster. -============== +-------------- Task Structure -============== +-------------- Splicer is made up of several subtasks. It is organized using both `ContainerTask `_ (sequential subtasks) and `ParallelTask `_ to divide work up and parallelize it It is organized like so: + * Splicer (!ContainerTask) + * Selector * ProcessProteinTask(!ParallelTask) + * FTPUpdate * Processor * SegmentBuilder --------- +^^^^^^^^ Selector --------- +^^^^^^^^ Selector downloads lists of pdbs from a source, currently `Dunbracks PICSES `_, and processes the list into a python object. @@ -31,22 +34,22 @@ There are two selectors: Both selectors filter on and parse protein level properties from the list. ----------- +^^^^^^^^^^ FTP Update ----------- +^^^^^^^^^^ Synchronizes a local cache of PDBs with a remote ftp server. By default it will synchronize all files located on the remote directory, optionally it can be given a list of PDB codes to synchronize. FTP Update compares dates to the milisecond. This requires using the MODTIME (mdtm) FTP command. ---------- +^^^^^^^^^ Processor ---------- +^^^^^^^^^ The processor parses a pdb file and extracts properties from it. The current implementation uses *BioPython* library and DSSP. This component is `covered in depth `_. ---------------- +^^^^^^^^^^^^^^^ Segment Builder ---------------- +^^^^^^^^^^^^^^^ This is a defunct subtask that was used to build the *de-normalized segment table* diff --git a/pgd/docs/source/sql_aggregate_functions.rst b/pgd/docs/source/sql_aggregate_functions.rst index 5dce388..10b4e4c 100644 --- a/pgd/docs/source/sql_aggregate_functions.rst +++ b/pgd/docs/source/sql_aggregate_functions.rst @@ -10,50 +10,57 @@ Django supports aggregate functions as of 1.1. Read more about it here: `Django Aggregate functions increase the speed of calculations because they are run on the data in place. Transferring data between the database server and application server requires significant overhead. -============================== +------------------------------ Statistics for Dihedral Angles -============================== +------------------------------ -.. image:: directional_average.png +.. .. image:: directional_average.png Dihedral angles require special function for average and standard deviation. The special function takes into account that the angles may wrap around from 180 to -180. Both of these functions work like any other aggregate functions. They have also been wrapped in a custom Django Aggregate function to work with django querysets. -======= +------- Average -======= +------- :: IF(DEGREES(ATAN2( -AVG(SIN(RADIANS(ome))) ,-AVG(COS(RADIANS(ome)))) + ) < 0 ,DEGREES(ATAN2( + -AVG(SIN(RADIANS(ome))) ,-AVG(COS(RADIANS(ome)))) + ) + 180 ,DEGREES(ATAN2( + -AVG(SIN(RADIANS(ome))) ,-AVG(COS(RADIANS(ome)))) + ) - 180 + ) AS ome_avg This works by converting the value into vectors. It adjusts the angles by +180 or -180 depending on whether it is a positive or negative angle. This shifts the vectors into the same space so that they may be averaged. -================== +------------------ Standard Deviation -================== +------------------ :: SQRT( IF (((ome+360)%360 - avgs.ome_avg) < 180 ,SUM(POW((ome+360)%360-avgs.ome_avg, 2)) ,SUM(POW(360-((ome+360)%360-avgs.ome_avg),2)) + )/(COUNT(ome)-1)) AS OME_STDDEV This function is very similar to a normal standard deviation calculation. The only difference is that a dihedral angle can have two deviations, the short and long way around the circle. We always want to use the shortest distance. ------------------ +^^^^^^^^^^^^^^^^^ Average Selection ------------------ +^^^^^^^^^^^^^^^^^ This standard deviation aggregate requires that the average be passed in. There are only two ways to match a list of averages to groups, subqueries or case logic. Neither is an ideal solution but case logic is the lesser of two evils. :: diff --git a/pgd/docs/source/sql_indexes.rst b/pgd/docs/source/sql_indexes.rst index 016987a..52abced 100644 --- a/pgd/docs/source/sql_indexes.rst +++ b/pgd/docs/source/sql_indexes.rst @@ -6,27 +6,27 @@ Like any database PGD relies on SQL Indexes for improved performance. This is a Please see the section on `Memory Table Indexes `_ for more information about the types of indexes used with memory tables. -======= +------- Protein -======= +------- ------------------ +^^^^^^^^^^^^^^^^^ Primary Key Index ------------------ +^^^^^^^^^^^^^^^^^ The primary key index is used when specific proteins have been selected by Code (primary key) ----------------- +^^^^^^^^^^^^^^^^ Resolution Index ----------------- +^^^^^^^^^^^^^^^^ The index on resolution is used in most cases. It filters large sets of proteins. The default query, with resolution <= 1.2 reduces the number of proteins from 16,000 to 2500. As the number of proteins nears the total number of proteins MySQL will switch to performing a full table scan. Even with indexes on other fields it does not appear to use them. --------------- +^^^^^^^^^^^^^^ Failed Indexes --------------- +^^^^^^^^^^^^^^ We also attempted to create indexes with resolution and other fields. No noticeable increase was detected, MySQL always opted for the individual Resolution Index. @@ -36,21 +36,21 @@ Protein Joined to Residue When joining a Residue with its Protein an index on Residue.protein_id is used --------------- +^^^^^^^^^^^^^^ Failed Indexes --------------- +^^^^^^^^^^^^^^ We attenmpted to add additional fields to the protein_id index. It was actually slower than the protein_id index alone. -========================= +------------------------- Residue Joined to Residue -========================= +------------------------- Residues are joined to Residues for the previous and next relationships using the Primary Key index on Residue. --------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Note on Join Direction for Previous and Next --------------------------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Residues join from **residue_0.next** to **residue_1.id** :: diff --git a/pgd/docs/source/technologies.rst b/pgd/docs/source/technologies.rst index 42fc600..243dbd7 100644 --- a/pgd/docs/source/technologies.rst +++ b/pgd/docs/source/technologies.rst @@ -2,9 +2,9 @@ Technologies Used By Protein Geometry Database ********************************************** -====== +------ Django -====== +------ Django is a high-level Python Web framework that encourages rapid development and clean, pragmatic design. Django provides some of the following features that are useful to PGD: @@ -14,24 +14,24 @@ Django is a high-level Python Web framework that encourages rapid development an Read more at: djangoproject.com -====== +------ jQuery -====== +------ jQuery is a fast and concise JavaScript Library that simplifies HTML document traversing, event handling, animating, and Ajax interactions for rapid web development. jQuery is used extensively in the front end to provide a "web 2.0" experience with dynamically updating pages. Read more at: jquery.com -======= +------- Raphael -======= +------- Raphaël is a small JavaScript library that should simplify your work with vector graphics on the web. Raphael is cross browser supported and is used to render graphs within PGD. Read more at: raphaeljs.com -===== +----- Mysql -===== +----- Mysql is an opensource database diff --git a/pgd/docs/source/terminology.rst b/pgd/docs/source/terminology.rst index e5dbe93..f65affb 100644 --- a/pgd/docs/source/terminology.rst +++ b/pgd/docs/source/terminology.rst @@ -1,6 +1,6 @@ -================= +***************** Terminology (WIP) -================= +***************** Protein * diff --git a/pgd/docs/source/updating_protein_database.rst b/pgd/docs/source/updating_protein_database.rst index 260dfa4..57d2460 100644 --- a/pgd/docs/source/updating_protein_database.rst +++ b/pgd/docs/source/updating_protein_database.rst @@ -1,14 +1,18 @@ +************************* +Updating Protein Database +************************* + The PGD database is updated approximately four times per year. The following process should be followed to update the database. One week before update: generate new selection file and download majority of updates. - *From the pgd-staging Django site:: + *From the pgd-staging Django site*:: - 1 > python manage.py fetch --report=date-report.txt --selection=date-selection.txt* + 1 > python manage.py fetch --report=date-report.txt --selection=date-selection.txt One day before update: reset staging database and load new selection file. - *From the pgd-staging Django site:: + *From the pgd-staging Django site*:: 1 > python manage.py shell @@ -17,40 +21,40 @@ One day before update: reset staging database and load new selection file. 2 > for p in Protein.objects.all(): 3 > p.delete() - 1 > ./pgd_splicer/ProcessPDBTask.py --pipein < date-selection.txt* + 1 > ./pgd_splicer/ProcessPDBTask.py --pipein < date-selection.txt On the update day: do the update! - *From the pgd-staging Django site:: + *From the pgd-staging Django site*:: 1 > ./pgd_splicer/dunbrack_selector.py --pipeout > selection.txt 2 > ./pgd_splicer/ftpupdate.py --pipein < selection.txt - 3 > ./pgd_splicer/ProcessPDBTask.py --pipein < selection.txt* + 3 > ./pgd_splicer/ProcessPDBTask.py --pipein < selection.txt That last command should be run within script so the output can be examined for particular failure modes once the command is complete. Error messages and recommended actions: * "CRC check failed", "local variable 'i' referenced before assignment", "KeyError": collect all codes with these message, delete the files associated with these codes, retrieve them again from the server, and attempt to import the files again. Example command lines for codes 1B12 and 4AMW:: - 1 > (cd ./pdb && for code in 1b12 4amw; do rm pdb$code.ent.gz; done) - 2 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ftpupdate.py --pipein - 3 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ProcessPDBTask.py --pipein + 1 > (cd ./pdb && for code in 1b12 4amw; do rm pdb$code.ent.gz; done) + 2 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ftpupdate.py --pipein + 3 > egrep \(1B12\|4AMW\) selection.txt | ./pgd_splicer/ProcessPDBTask.py --pipein If the errors persist, document offending codes in bug reports as appropriate and copy the retrieved files aside for testing and comparison. * "Structure/DSSP mismatch", "No chains were parsed!": document offending codes in update post to PGD mailing list. Cross-check database against selection file. - *From the pgd-staging Django site:: + *From the pgd-staging Django site*:: - 1 > python manage.py crosscheck --selection=selection.txt* + 1 > python manage.py crosscheck --selection=selection.txt The staging site is now ready for customer preview. Promote database from staging to production. - *From the pgd-prod Django site:: + *From the pgd-prod Django site*:: - 1 > ./update-from-staging.sh* + 1 > ./update-from-staging.sh Rename the selection file for archival purposes. :: From 5935dc401071ebbf8f6ed38a879e50c5bdb31c59 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Mon, 29 Sep 2014 09:09:29 -0700 Subject: [PATCH 17/28] Licensed PGD under the Apache License, version 2.0. Added LICENSE.txt which contains the software license for the project. --- pgd/LICENSE.txt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 pgd/LICENSE.txt diff --git a/pgd/LICENSE.txt b/pgd/LICENSE.txt new file mode 100644 index 0000000..a355615 --- /dev/null +++ b/pgd/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2014 Oregon State University + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From 40e307a56e698e7d107e6fe0cb2d3457a26f195b Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Mon, 29 Sep 2014 09:28:52 -0700 Subject: [PATCH 18/28] Reordered repository. The previous repository stored everything one directory lower for no apparent reason. --- pgd/.gitignore => .gitignore | 0 pgd/LICENSE.txt => LICENSE.txt | 0 pgd/__init__.py => __init__.py | 0 pgd/context_processors.py => context_processors.py | 0 {pgd/docs => docs}/Makefile | 0 {pgd/docs => docs}/source/browse.rst | 0 {pgd/docs => docs}/source/code_modules.rst | 0 {pgd/docs => docs}/source/conf.py | 0 {pgd/docs => docs}/source/data_dump.rst | 0 .../source/denormalized_search_table.rst | 0 {pgd/docs => docs}/source/developing_splicer.rst | 0 {pgd/docs => docs}/source/development_workflow.rst | 0 {pgd/docs => docs}/source/hotfix_workflow.rst | 0 {pgd/docs => docs}/source/importing_data.rst | 0 {pgd/docs => docs}/source/in_memory_tables.rst | 0 {pgd/docs => docs}/source/index.rst | 0 {pgd/docs => docs}/source/installation.rst | 0 .../source/management_command_design.rst | 0 {pgd/docs => docs}/source/optimization.rst | 0 {pgd/docs => docs}/source/processor_subtask.rst | 0 {pgd/docs => docs}/source/protein_data_models.rst | 0 {pgd/docs => docs}/source/ramachandran_plot.rst | 0 {pgd/docs => docs}/source/release_workflow.rst | 0 {pgd/docs => docs}/source/running_splicer.rst | 0 .../source/running_splicer_from_command_line.rst | 0 {pgd/docs => docs}/source/search_statistics.rst | 0 {pgd/docs => docs}/source/search_workflow.rst | 0 .../source/site_specific_information.rst | 0 {pgd/docs => docs}/source/splicer.rst | 0 .../source/sql_aggregate_functions.rst | 0 {pgd/docs => docs}/source/sql_indexes.rst | 0 {pgd/docs => docs}/source/technologies.rst | 0 {pgd/docs => docs}/source/terminology.rst | 0 .../source/updating_protein_database.rst | 0 pgd/fabfile.py => fabfile.py | 0 pgd/manage.py => manage.py | 0 pgd/pgd_constants.py => pgd_constants.py | 0 {pgd/pgd_core => pgd_core}/__init__.py | 0 {pgd/pgd_core => pgd_core}/models.py | 0 {pgd/pgd_core => pgd_core}/views.py | 0 {pgd/pgd_search => pgd_search}/__init__.py | 0 {pgd/pgd_search => pgd_search}/browse/__init__.py | 0 {pgd/pgd_search => pgd_search}/browse/views.py | 0 {pgd/pgd_search => pgd_search}/dump/DataDump.py | 0 {pgd/pgd_search => pgd_search}/dump/__init__.py | 0 {pgd/pgd_search => pgd_search}/dump/views.py | 0 .../histogram/Histogram.py | 0 .../pgd_search => pgd_search}/histogram/__init__.py | 0 {pgd/pgd_search => pgd_search}/histogram/svg.py | 0 {pgd/pgd_search => pgd_search}/histogram/views.py | 0 {pgd/pgd_search => pgd_search}/models.py | 0 .../pgd_search => pgd_search}/plot/ConfDistFuncs.py | 0 {pgd/pgd_search => pgd_search}/plot/PlotForm.py | 0 {pgd/pgd_search => pgd_search}/plot/__init__.py | 0 {pgd/pgd_search => pgd_search}/plot/svg.py | 0 {pgd/pgd_search => pgd_search}/plot/views.py | 0 {pgd/pgd_search => pgd_search}/search/SearchForm.py | 0 {pgd/pgd_search => pgd_search}/search/__init__.py | 0 {pgd/pgd_search => pgd_search}/search/views.py | 0 {pgd/pgd_search => pgd_search}/segment.py | 0 .../statistics/__init__.py | 0 .../statistics/aggregates.py | 0 .../statistics/directional_stddev.py | 0 {pgd/pgd_search => pgd_search}/statistics/form.py | 0 {pgd/pgd_search => pgd_search}/statistics/views.py | 0 .../templates/angles_helper.html | 0 .../pgd_search => pgd_search}/templates/browse.html | 0 .../templates/chi_help.html | 0 {pgd/pgd_search => pgd_search}/templates/graph.html | 0 {pgd/pgd_search => pgd_search}/templates/help.html | 0 .../templates/qtiphelp.html | 0 .../templates/saveSearch.html | 0 {pgd/pgd_search => pgd_search}/templates/saved.html | 0 .../pgd_search => pgd_search}/templates/search.html | 0 .../templates/search_base.html | 0 {pgd/pgd_search => pgd_search}/templates/stats.html | 0 .../templates/stats_a.html | 0 .../templatetags/__init__.py | 0 .../templatetags/search_extras.py | 0 {pgd/pgd_search => pgd_search}/tester.py | 0 {pgd/pgd_search => pgd_search}/tests.py | 0 {pgd/pgd_search => pgd_search}/urls.py | 0 {pgd/pgd_search => pgd_search}/views.py | 0 {pgd/pgd_splicer => pgd_splicer}/ProcessPDBTask.py | 0 {pgd/pgd_splicer => pgd_splicer}/SegmentBuilder.py | 0 {pgd/pgd_splicer => pgd_splicer}/__init__.py | 0 {pgd/pgd_splicer => pgd_splicer}/chi.py | 0 {pgd/pgd_splicer => pgd_splicer}/compareDump.py | 0 .../compare_protein_dump.py | 0 {pgd/pgd_splicer => pgd_splicer}/dbConvert.sql | 0 {pgd/pgd_splicer => pgd_splicer}/dump_protein.py | 0 .../dunbrack_selector.py | 0 .../fixtures/pgd_core.json | 0 {pgd/pgd_splicer => pgd_splicer}/ftpupdate.py | 0 .../management/__init__.py | 0 .../management/commands/__init__.py | 0 .../management/commands/crosscheck.py | 0 .../management/commands/fetch.py | 0 {pgd/pgd_splicer => pgd_splicer}/models.py | 0 {pgd/pgd_splicer => pgd_splicer}/process_protein.py | 0 {pgd/pgd_splicer => pgd_splicer}/selector.py | 0 {pgd/pgd_splicer => pgd_splicer}/sidechain.py | 0 {pgd/pgd_splicer => pgd_splicer}/splicer.py | 0 .../testfiles/cullpdb_crosscheck.txt | 0 .../testfiles/cullpdb_pc25.gz | Bin .../testfiles/cullpdb_pc90.gz | Bin .../testfiles/cullpdb_selection.txt | 0 .../testfiles/fixture_crosscheck.txt | 0 .../testfiles/fixture_selection.txt | 0 .../testfiles/pdb1mwq.ent.gz | Bin .../testfiles/pdb1mww.ent.gz | Bin .../testfiles/pdb1twf.ent.gz | Bin .../testfiles/pdb3cgm.ent.gz | Bin .../testfiles/pdb3cgx.ent.gz | Bin .../testfiles/pdb3cgz.ent.gz | Bin .../testfiles/selection_page.txt | 0 {pgd/pgd_splicer => pgd_splicer}/tests.py | 0 pgd/requirements.txt => requirements.txt | 0 pgd/settings.py.dist => settings.py.dist | 0 pgd/spliceTest.py => spliceTest.py | 0 {pgd/static => static}/css/jquery.autocomplete.css | 0 {pgd/static => static}/css/jquery.svg.css | 0 {pgd/static => static}/css/main.css | 0 {pgd/static => static}/css/search.css | 0 ...t-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png | Bin {pgd/static => static}/images/ajax-error.png | Bin {pgd/static => static}/images/ajax-loader.gif | Bin {pgd/static => static}/images/delete.png | Bin {pgd/static => static}/images/edit.png | Bin .../images/help/angle_C-1_N_Ca.png | Bin .../images/help/angle_Ca_C_N+1.png | Bin {pgd/static => static}/images/help/angle_Ca_C_O.png | Bin .../static => static}/images/help/angle_Cb_Ca_C.png | Bin {pgd/static => static}/images/help/angle_N_Ca_C.png | Bin .../static => static}/images/help/angle_N_Ca_Cb.png | Bin .../static => static}/images/help/angle_O_C_N+1.png | Bin {pgd/static => static}/images/help/length_C-1_N.png | Bin {pgd/static => static}/images/help/length_C_N+1.png | Bin {pgd/static => static}/images/help/length_C_O.png | Bin {pgd/static => static}/images/help/length_Ca_C.png | Bin {pgd/static => static}/images/help/length_Ca_Cb.png | Bin {pgd/static => static}/images/help/length_N-Ca.png | Bin {pgd/static => static}/images/help/ome.png | Bin {pgd/static => static}/images/help/phi.png | Bin {pgd/static => static}/images/help/psi.png | Bin {pgd/static => static}/images/help/segment.png | Bin {pgd/static => static}/images/help/zeta.png | Bin {pgd/static => static}/images/icons/eds.ico | Bin {pgd/static => static}/images/icons/pdb.ico | Bin {pgd/static => static}/images/icons/pdbsum.ico | Bin .../images/linear-groups-ramach.png | Bin {pgd/static => static}/images/minus2.png | Bin .../images/monopeptide-inkscape.png | Bin {pgd/static => static}/images/plus2.png | Bin {pgd/static => static}/js/DejaVu_400.font.full.js | 0 {pgd/static => static}/js/DejaVu_400.font.js | 0 {pgd/static => static}/js/jqbrowser.js | 0 {pgd/static => static}/js/jquery-1.2.6.js | 0 {pgd/static => static}/js/jquery-1.3.2.js | 0 {pgd/static => static}/js/jquery-1.4.1.min.js | 0 .../js/jquery.acts_as_tree_table.js | 0 .../js/jquery.autocomplete.pack.js | 0 {pgd/static => static}/js/jquery.progressbar.js | 0 .../js/jquery.qtip-1.0.0-rc3.min.js | 0 {pgd/static => static}/js/jquery.stylish-select.js | 0 .../static => static}/js/jquery.svg.compat-1.0.1.js | 0 {pgd/static => static}/js/jquery.svg.js | 0 {pgd/static => static}/js/jquery.svg.min.js | 0 {pgd/static => static}/js/jquery.svg.pack.js | 0 {pgd/static => static}/js/jquery.svganim.js | 0 {pgd/static => static}/js/jquery.svganim.min.js | 0 {pgd/static => static}/js/jquery.svganim.pack.js | 0 {pgd/static => static}/js/jquery.svgfilter.js | 0 {pgd/static => static}/js/jquery.svgfilter.min.js | 0 {pgd/static => static}/js/jquery.svgfilter.pack.js | 0 {pgd/static => static}/js/jquery.svggraph.js | 0 {pgd/static => static}/js/jquery.svggraph.min.js | 0 {pgd/static => static}/js/jquery.svggraph.pack.js | 0 {pgd/static => static}/js/pgd.svg.js | 0 {pgd/static => static}/js/raphael-min.js | 0 {pgd/static => static}/js/search.js | 0 {pgd/static => static}/pdf/2009_berkholz.pdf | Bin .../pdf/2009_berkholz_perspective.pdf | Bin {pgd/static => static}/pdf/Berkholz-PGD-2010.pdf | Bin .../pdf/Hollingsworth-etal_2009.pdf | Bin {pgd/static => static}/pdf/Karplus_2008_letter.pdf | Bin {pgd/templates => templates}/404.html | 0 {pgd/templates => templates}/500.html | 0 {pgd/templates => templates}/base.html | 0 {pgd/templates => templates}/contactus.html | 0 {pgd/templates => templates}/news.html | 0 {pgd/templates => templates}/references.html | 0 .../registration/activate.html | 0 .../registration/activation_email.txt | 0 .../registration/activation_email_subject.txt | 0 .../templates => templates}/registration/login.html | 0 .../registration/logout.html | 0 .../registration/password_change_done.html | 0 .../registration/password_change_form.html | 0 .../registration/password_reset_complete.html | 0 .../registration/password_reset_confirm.html | 0 .../registration/password_reset_done.html | 0 .../registration/password_reset_email.html | 0 .../registration/password_reset_form.html | 0 .../registration/registration_complete.html | 0 .../registration/registration_form.html | 0 {pgd/templates => templates}/welcome.html | 0 pgd/urls.py => urls.py | 0 208 files changed, 0 insertions(+), 0 deletions(-) rename pgd/.gitignore => .gitignore (100%) rename pgd/LICENSE.txt => LICENSE.txt (100%) rename pgd/__init__.py => __init__.py (100%) rename pgd/context_processors.py => context_processors.py (100%) rename {pgd/docs => docs}/Makefile (100%) rename {pgd/docs => docs}/source/browse.rst (100%) rename {pgd/docs => docs}/source/code_modules.rst (100%) rename {pgd/docs => docs}/source/conf.py (100%) rename {pgd/docs => docs}/source/data_dump.rst (100%) rename {pgd/docs => docs}/source/denormalized_search_table.rst (100%) rename {pgd/docs => docs}/source/developing_splicer.rst (100%) rename {pgd/docs => docs}/source/development_workflow.rst (100%) rename {pgd/docs => docs}/source/hotfix_workflow.rst (100%) rename {pgd/docs => docs}/source/importing_data.rst (100%) rename {pgd/docs => docs}/source/in_memory_tables.rst (100%) rename {pgd/docs => docs}/source/index.rst (100%) rename {pgd/docs => docs}/source/installation.rst (100%) rename {pgd/docs => docs}/source/management_command_design.rst (100%) rename {pgd/docs => docs}/source/optimization.rst (100%) rename {pgd/docs => docs}/source/processor_subtask.rst (100%) rename {pgd/docs => docs}/source/protein_data_models.rst (100%) rename {pgd/docs => docs}/source/ramachandran_plot.rst (100%) rename {pgd/docs => docs}/source/release_workflow.rst (100%) rename {pgd/docs => docs}/source/running_splicer.rst (100%) rename {pgd/docs => docs}/source/running_splicer_from_command_line.rst (100%) rename {pgd/docs => docs}/source/search_statistics.rst (100%) rename {pgd/docs => docs}/source/search_workflow.rst (100%) rename {pgd/docs => docs}/source/site_specific_information.rst (100%) rename {pgd/docs => docs}/source/splicer.rst (100%) rename {pgd/docs => docs}/source/sql_aggregate_functions.rst (100%) rename {pgd/docs => docs}/source/sql_indexes.rst (100%) rename {pgd/docs => docs}/source/technologies.rst (100%) rename {pgd/docs => docs}/source/terminology.rst (100%) rename {pgd/docs => docs}/source/updating_protein_database.rst (100%) rename pgd/fabfile.py => fabfile.py (100%) rename pgd/manage.py => manage.py (100%) rename pgd/pgd_constants.py => pgd_constants.py (100%) rename {pgd/pgd_core => pgd_core}/__init__.py (100%) rename {pgd/pgd_core => pgd_core}/models.py (100%) rename {pgd/pgd_core => pgd_core}/views.py (100%) rename {pgd/pgd_search => pgd_search}/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/browse/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/browse/views.py (100%) rename {pgd/pgd_search => pgd_search}/dump/DataDump.py (100%) rename {pgd/pgd_search => pgd_search}/dump/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/dump/views.py (100%) rename {pgd/pgd_search => pgd_search}/histogram/Histogram.py (100%) rename {pgd/pgd_search => pgd_search}/histogram/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/histogram/svg.py (100%) rename {pgd/pgd_search => pgd_search}/histogram/views.py (100%) rename {pgd/pgd_search => pgd_search}/models.py (100%) rename {pgd/pgd_search => pgd_search}/plot/ConfDistFuncs.py (100%) rename {pgd/pgd_search => pgd_search}/plot/PlotForm.py (100%) rename {pgd/pgd_search => pgd_search}/plot/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/plot/svg.py (100%) rename {pgd/pgd_search => pgd_search}/plot/views.py (100%) rename {pgd/pgd_search => pgd_search}/search/SearchForm.py (100%) rename {pgd/pgd_search => pgd_search}/search/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/search/views.py (100%) rename {pgd/pgd_search => pgd_search}/segment.py (100%) rename {pgd/pgd_search => pgd_search}/statistics/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/statistics/aggregates.py (100%) rename {pgd/pgd_search => pgd_search}/statistics/directional_stddev.py (100%) rename {pgd/pgd_search => pgd_search}/statistics/form.py (100%) rename {pgd/pgd_search => pgd_search}/statistics/views.py (100%) rename {pgd/pgd_search => pgd_search}/templates/angles_helper.html (100%) rename {pgd/pgd_search => pgd_search}/templates/browse.html (100%) rename {pgd/pgd_search => pgd_search}/templates/chi_help.html (100%) rename {pgd/pgd_search => pgd_search}/templates/graph.html (100%) rename {pgd/pgd_search => pgd_search}/templates/help.html (100%) rename {pgd/pgd_search => pgd_search}/templates/qtiphelp.html (100%) rename {pgd/pgd_search => pgd_search}/templates/saveSearch.html (100%) rename {pgd/pgd_search => pgd_search}/templates/saved.html (100%) rename {pgd/pgd_search => pgd_search}/templates/search.html (100%) rename {pgd/pgd_search => pgd_search}/templates/search_base.html (100%) rename {pgd/pgd_search => pgd_search}/templates/stats.html (100%) rename {pgd/pgd_search => pgd_search}/templates/stats_a.html (100%) rename {pgd/pgd_search => pgd_search}/templatetags/__init__.py (100%) rename {pgd/pgd_search => pgd_search}/templatetags/search_extras.py (100%) rename {pgd/pgd_search => pgd_search}/tester.py (100%) rename {pgd/pgd_search => pgd_search}/tests.py (100%) rename {pgd/pgd_search => pgd_search}/urls.py (100%) rename {pgd/pgd_search => pgd_search}/views.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/ProcessPDBTask.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/SegmentBuilder.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/__init__.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/chi.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/compareDump.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/compare_protein_dump.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/dbConvert.sql (100%) rename {pgd/pgd_splicer => pgd_splicer}/dump_protein.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/dunbrack_selector.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/fixtures/pgd_core.json (100%) rename {pgd/pgd_splicer => pgd_splicer}/ftpupdate.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/management/__init__.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/management/commands/__init__.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/management/commands/crosscheck.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/management/commands/fetch.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/models.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/process_protein.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/selector.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/sidechain.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/splicer.py (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/cullpdb_crosscheck.txt (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/cullpdb_pc25.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/cullpdb_pc90.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/cullpdb_selection.txt (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/fixture_crosscheck.txt (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/fixture_selection.txt (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb1mwq.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb1mww.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb1twf.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb3cgm.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb3cgx.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/pdb3cgz.ent.gz (100%) rename {pgd/pgd_splicer => pgd_splicer}/testfiles/selection_page.txt (100%) rename {pgd/pgd_splicer => pgd_splicer}/tests.py (100%) rename pgd/requirements.txt => requirements.txt (100%) rename pgd/settings.py.dist => settings.py.dist (100%) rename pgd/spliceTest.py => spliceTest.py (100%) rename {pgd/static => static}/css/jquery.autocomplete.css (100%) rename {pgd/static => static}/css/jquery.svg.css (100%) rename {pgd/static => static}/css/main.css (100%) rename {pgd/static => static}/css/search.css (100%) rename {pgd/static => static}/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png (100%) rename {pgd/static => static}/images/ajax-error.png (100%) rename {pgd/static => static}/images/ajax-loader.gif (100%) rename {pgd/static => static}/images/delete.png (100%) rename {pgd/static => static}/images/edit.png (100%) rename {pgd/static => static}/images/help/angle_C-1_N_Ca.png (100%) rename {pgd/static => static}/images/help/angle_Ca_C_N+1.png (100%) rename {pgd/static => static}/images/help/angle_Ca_C_O.png (100%) rename {pgd/static => static}/images/help/angle_Cb_Ca_C.png (100%) rename {pgd/static => static}/images/help/angle_N_Ca_C.png (100%) rename {pgd/static => static}/images/help/angle_N_Ca_Cb.png (100%) rename {pgd/static => static}/images/help/angle_O_C_N+1.png (100%) rename {pgd/static => static}/images/help/length_C-1_N.png (100%) rename {pgd/static => static}/images/help/length_C_N+1.png (100%) rename {pgd/static => static}/images/help/length_C_O.png (100%) rename {pgd/static => static}/images/help/length_Ca_C.png (100%) rename {pgd/static => static}/images/help/length_Ca_Cb.png (100%) rename {pgd/static => static}/images/help/length_N-Ca.png (100%) rename {pgd/static => static}/images/help/ome.png (100%) rename {pgd/static => static}/images/help/phi.png (100%) rename {pgd/static => static}/images/help/psi.png (100%) rename {pgd/static => static}/images/help/segment.png (100%) rename {pgd/static => static}/images/help/zeta.png (100%) rename {pgd/static => static}/images/icons/eds.ico (100%) rename {pgd/static => static}/images/icons/pdb.ico (100%) rename {pgd/static => static}/images/icons/pdbsum.ico (100%) rename {pgd/static => static}/images/linear-groups-ramach.png (100%) rename {pgd/static => static}/images/minus2.png (100%) rename {pgd/static => static}/images/monopeptide-inkscape.png (100%) rename {pgd/static => static}/images/plus2.png (100%) rename {pgd/static => static}/js/DejaVu_400.font.full.js (100%) rename {pgd/static => static}/js/DejaVu_400.font.js (100%) rename {pgd/static => static}/js/jqbrowser.js (100%) rename {pgd/static => static}/js/jquery-1.2.6.js (100%) rename {pgd/static => static}/js/jquery-1.3.2.js (100%) rename {pgd/static => static}/js/jquery-1.4.1.min.js (100%) rename {pgd/static => static}/js/jquery.acts_as_tree_table.js (100%) rename {pgd/static => static}/js/jquery.autocomplete.pack.js (100%) rename {pgd/static => static}/js/jquery.progressbar.js (100%) rename {pgd/static => static}/js/jquery.qtip-1.0.0-rc3.min.js (100%) rename {pgd/static => static}/js/jquery.stylish-select.js (100%) rename {pgd/static => static}/js/jquery.svg.compat-1.0.1.js (100%) rename {pgd/static => static}/js/jquery.svg.js (100%) rename {pgd/static => static}/js/jquery.svg.min.js (100%) rename {pgd/static => static}/js/jquery.svg.pack.js (100%) rename {pgd/static => static}/js/jquery.svganim.js (100%) rename {pgd/static => static}/js/jquery.svganim.min.js (100%) rename {pgd/static => static}/js/jquery.svganim.pack.js (100%) rename {pgd/static => static}/js/jquery.svgfilter.js (100%) rename {pgd/static => static}/js/jquery.svgfilter.min.js (100%) rename {pgd/static => static}/js/jquery.svgfilter.pack.js (100%) rename {pgd/static => static}/js/jquery.svggraph.js (100%) rename {pgd/static => static}/js/jquery.svggraph.min.js (100%) rename {pgd/static => static}/js/jquery.svggraph.pack.js (100%) rename {pgd/static => static}/js/pgd.svg.js (100%) rename {pgd/static => static}/js/raphael-min.js (100%) rename {pgd/static => static}/js/search.js (100%) rename {pgd/static => static}/pdf/2009_berkholz.pdf (100%) rename {pgd/static => static}/pdf/2009_berkholz_perspective.pdf (100%) rename {pgd/static => static}/pdf/Berkholz-PGD-2010.pdf (100%) rename {pgd/static => static}/pdf/Hollingsworth-etal_2009.pdf (100%) rename {pgd/static => static}/pdf/Karplus_2008_letter.pdf (100%) rename {pgd/templates => templates}/404.html (100%) rename {pgd/templates => templates}/500.html (100%) rename {pgd/templates => templates}/base.html (100%) rename {pgd/templates => templates}/contactus.html (100%) rename {pgd/templates => templates}/news.html (100%) rename {pgd/templates => templates}/references.html (100%) rename {pgd/templates => templates}/registration/activate.html (100%) rename {pgd/templates => templates}/registration/activation_email.txt (100%) rename {pgd/templates => templates}/registration/activation_email_subject.txt (100%) rename {pgd/templates => templates}/registration/login.html (100%) rename {pgd/templates => templates}/registration/logout.html (100%) rename {pgd/templates => templates}/registration/password_change_done.html (100%) rename {pgd/templates => templates}/registration/password_change_form.html (100%) rename {pgd/templates => templates}/registration/password_reset_complete.html (100%) rename {pgd/templates => templates}/registration/password_reset_confirm.html (100%) rename {pgd/templates => templates}/registration/password_reset_done.html (100%) rename {pgd/templates => templates}/registration/password_reset_email.html (100%) rename {pgd/templates => templates}/registration/password_reset_form.html (100%) rename {pgd/templates => templates}/registration/registration_complete.html (100%) rename {pgd/templates => templates}/registration/registration_form.html (100%) rename {pgd/templates => templates}/welcome.html (100%) rename pgd/urls.py => urls.py (100%) diff --git a/pgd/.gitignore b/.gitignore similarity index 100% rename from pgd/.gitignore rename to .gitignore diff --git a/pgd/LICENSE.txt b/LICENSE.txt similarity index 100% rename from pgd/LICENSE.txt rename to LICENSE.txt diff --git a/pgd/__init__.py b/__init__.py similarity index 100% rename from pgd/__init__.py rename to __init__.py diff --git a/pgd/context_processors.py b/context_processors.py similarity index 100% rename from pgd/context_processors.py rename to context_processors.py diff --git a/pgd/docs/Makefile b/docs/Makefile similarity index 100% rename from pgd/docs/Makefile rename to docs/Makefile diff --git a/pgd/docs/source/browse.rst b/docs/source/browse.rst similarity index 100% rename from pgd/docs/source/browse.rst rename to docs/source/browse.rst diff --git a/pgd/docs/source/code_modules.rst b/docs/source/code_modules.rst similarity index 100% rename from pgd/docs/source/code_modules.rst rename to docs/source/code_modules.rst diff --git a/pgd/docs/source/conf.py b/docs/source/conf.py similarity index 100% rename from pgd/docs/source/conf.py rename to docs/source/conf.py diff --git a/pgd/docs/source/data_dump.rst b/docs/source/data_dump.rst similarity index 100% rename from pgd/docs/source/data_dump.rst rename to docs/source/data_dump.rst diff --git a/pgd/docs/source/denormalized_search_table.rst b/docs/source/denormalized_search_table.rst similarity index 100% rename from pgd/docs/source/denormalized_search_table.rst rename to docs/source/denormalized_search_table.rst diff --git a/pgd/docs/source/developing_splicer.rst b/docs/source/developing_splicer.rst similarity index 100% rename from pgd/docs/source/developing_splicer.rst rename to docs/source/developing_splicer.rst diff --git a/pgd/docs/source/development_workflow.rst b/docs/source/development_workflow.rst similarity index 100% rename from pgd/docs/source/development_workflow.rst rename to docs/source/development_workflow.rst diff --git a/pgd/docs/source/hotfix_workflow.rst b/docs/source/hotfix_workflow.rst similarity index 100% rename from pgd/docs/source/hotfix_workflow.rst rename to docs/source/hotfix_workflow.rst diff --git a/pgd/docs/source/importing_data.rst b/docs/source/importing_data.rst similarity index 100% rename from pgd/docs/source/importing_data.rst rename to docs/source/importing_data.rst diff --git a/pgd/docs/source/in_memory_tables.rst b/docs/source/in_memory_tables.rst similarity index 100% rename from pgd/docs/source/in_memory_tables.rst rename to docs/source/in_memory_tables.rst diff --git a/pgd/docs/source/index.rst b/docs/source/index.rst similarity index 100% rename from pgd/docs/source/index.rst rename to docs/source/index.rst diff --git a/pgd/docs/source/installation.rst b/docs/source/installation.rst similarity index 100% rename from pgd/docs/source/installation.rst rename to docs/source/installation.rst diff --git a/pgd/docs/source/management_command_design.rst b/docs/source/management_command_design.rst similarity index 100% rename from pgd/docs/source/management_command_design.rst rename to docs/source/management_command_design.rst diff --git a/pgd/docs/source/optimization.rst b/docs/source/optimization.rst similarity index 100% rename from pgd/docs/source/optimization.rst rename to docs/source/optimization.rst diff --git a/pgd/docs/source/processor_subtask.rst b/docs/source/processor_subtask.rst similarity index 100% rename from pgd/docs/source/processor_subtask.rst rename to docs/source/processor_subtask.rst diff --git a/pgd/docs/source/protein_data_models.rst b/docs/source/protein_data_models.rst similarity index 100% rename from pgd/docs/source/protein_data_models.rst rename to docs/source/protein_data_models.rst diff --git a/pgd/docs/source/ramachandran_plot.rst b/docs/source/ramachandran_plot.rst similarity index 100% rename from pgd/docs/source/ramachandran_plot.rst rename to docs/source/ramachandran_plot.rst diff --git a/pgd/docs/source/release_workflow.rst b/docs/source/release_workflow.rst similarity index 100% rename from pgd/docs/source/release_workflow.rst rename to docs/source/release_workflow.rst diff --git a/pgd/docs/source/running_splicer.rst b/docs/source/running_splicer.rst similarity index 100% rename from pgd/docs/source/running_splicer.rst rename to docs/source/running_splicer.rst diff --git a/pgd/docs/source/running_splicer_from_command_line.rst b/docs/source/running_splicer_from_command_line.rst similarity index 100% rename from pgd/docs/source/running_splicer_from_command_line.rst rename to docs/source/running_splicer_from_command_line.rst diff --git a/pgd/docs/source/search_statistics.rst b/docs/source/search_statistics.rst similarity index 100% rename from pgd/docs/source/search_statistics.rst rename to docs/source/search_statistics.rst diff --git a/pgd/docs/source/search_workflow.rst b/docs/source/search_workflow.rst similarity index 100% rename from pgd/docs/source/search_workflow.rst rename to docs/source/search_workflow.rst diff --git a/pgd/docs/source/site_specific_information.rst b/docs/source/site_specific_information.rst similarity index 100% rename from pgd/docs/source/site_specific_information.rst rename to docs/source/site_specific_information.rst diff --git a/pgd/docs/source/splicer.rst b/docs/source/splicer.rst similarity index 100% rename from pgd/docs/source/splicer.rst rename to docs/source/splicer.rst diff --git a/pgd/docs/source/sql_aggregate_functions.rst b/docs/source/sql_aggregate_functions.rst similarity index 100% rename from pgd/docs/source/sql_aggregate_functions.rst rename to docs/source/sql_aggregate_functions.rst diff --git a/pgd/docs/source/sql_indexes.rst b/docs/source/sql_indexes.rst similarity index 100% rename from pgd/docs/source/sql_indexes.rst rename to docs/source/sql_indexes.rst diff --git a/pgd/docs/source/technologies.rst b/docs/source/technologies.rst similarity index 100% rename from pgd/docs/source/technologies.rst rename to docs/source/technologies.rst diff --git a/pgd/docs/source/terminology.rst b/docs/source/terminology.rst similarity index 100% rename from pgd/docs/source/terminology.rst rename to docs/source/terminology.rst diff --git a/pgd/docs/source/updating_protein_database.rst b/docs/source/updating_protein_database.rst similarity index 100% rename from pgd/docs/source/updating_protein_database.rst rename to docs/source/updating_protein_database.rst diff --git a/pgd/fabfile.py b/fabfile.py similarity index 100% rename from pgd/fabfile.py rename to fabfile.py diff --git a/pgd/manage.py b/manage.py similarity index 100% rename from pgd/manage.py rename to manage.py diff --git a/pgd/pgd_constants.py b/pgd_constants.py similarity index 100% rename from pgd/pgd_constants.py rename to pgd_constants.py diff --git a/pgd/pgd_core/__init__.py b/pgd_core/__init__.py similarity index 100% rename from pgd/pgd_core/__init__.py rename to pgd_core/__init__.py diff --git a/pgd/pgd_core/models.py b/pgd_core/models.py similarity index 100% rename from pgd/pgd_core/models.py rename to pgd_core/models.py diff --git a/pgd/pgd_core/views.py b/pgd_core/views.py similarity index 100% rename from pgd/pgd_core/views.py rename to pgd_core/views.py diff --git a/pgd/pgd_search/__init__.py b/pgd_search/__init__.py similarity index 100% rename from pgd/pgd_search/__init__.py rename to pgd_search/__init__.py diff --git a/pgd/pgd_search/browse/__init__.py b/pgd_search/browse/__init__.py similarity index 100% rename from pgd/pgd_search/browse/__init__.py rename to pgd_search/browse/__init__.py diff --git a/pgd/pgd_search/browse/views.py b/pgd_search/browse/views.py similarity index 100% rename from pgd/pgd_search/browse/views.py rename to pgd_search/browse/views.py diff --git a/pgd/pgd_search/dump/DataDump.py b/pgd_search/dump/DataDump.py similarity index 100% rename from pgd/pgd_search/dump/DataDump.py rename to pgd_search/dump/DataDump.py diff --git a/pgd/pgd_search/dump/__init__.py b/pgd_search/dump/__init__.py similarity index 100% rename from pgd/pgd_search/dump/__init__.py rename to pgd_search/dump/__init__.py diff --git a/pgd/pgd_search/dump/views.py b/pgd_search/dump/views.py similarity index 100% rename from pgd/pgd_search/dump/views.py rename to pgd_search/dump/views.py diff --git a/pgd/pgd_search/histogram/Histogram.py b/pgd_search/histogram/Histogram.py similarity index 100% rename from pgd/pgd_search/histogram/Histogram.py rename to pgd_search/histogram/Histogram.py diff --git a/pgd/pgd_search/histogram/__init__.py b/pgd_search/histogram/__init__.py similarity index 100% rename from pgd/pgd_search/histogram/__init__.py rename to pgd_search/histogram/__init__.py diff --git a/pgd/pgd_search/histogram/svg.py b/pgd_search/histogram/svg.py similarity index 100% rename from pgd/pgd_search/histogram/svg.py rename to pgd_search/histogram/svg.py diff --git a/pgd/pgd_search/histogram/views.py b/pgd_search/histogram/views.py similarity index 100% rename from pgd/pgd_search/histogram/views.py rename to pgd_search/histogram/views.py diff --git a/pgd/pgd_search/models.py b/pgd_search/models.py similarity index 100% rename from pgd/pgd_search/models.py rename to pgd_search/models.py diff --git a/pgd/pgd_search/plot/ConfDistFuncs.py b/pgd_search/plot/ConfDistFuncs.py similarity index 100% rename from pgd/pgd_search/plot/ConfDistFuncs.py rename to pgd_search/plot/ConfDistFuncs.py diff --git a/pgd/pgd_search/plot/PlotForm.py b/pgd_search/plot/PlotForm.py similarity index 100% rename from pgd/pgd_search/plot/PlotForm.py rename to pgd_search/plot/PlotForm.py diff --git a/pgd/pgd_search/plot/__init__.py b/pgd_search/plot/__init__.py similarity index 100% rename from pgd/pgd_search/plot/__init__.py rename to pgd_search/plot/__init__.py diff --git a/pgd/pgd_search/plot/svg.py b/pgd_search/plot/svg.py similarity index 100% rename from pgd/pgd_search/plot/svg.py rename to pgd_search/plot/svg.py diff --git a/pgd/pgd_search/plot/views.py b/pgd_search/plot/views.py similarity index 100% rename from pgd/pgd_search/plot/views.py rename to pgd_search/plot/views.py diff --git a/pgd/pgd_search/search/SearchForm.py b/pgd_search/search/SearchForm.py similarity index 100% rename from pgd/pgd_search/search/SearchForm.py rename to pgd_search/search/SearchForm.py diff --git a/pgd/pgd_search/search/__init__.py b/pgd_search/search/__init__.py similarity index 100% rename from pgd/pgd_search/search/__init__.py rename to pgd_search/search/__init__.py diff --git a/pgd/pgd_search/search/views.py b/pgd_search/search/views.py similarity index 100% rename from pgd/pgd_search/search/views.py rename to pgd_search/search/views.py diff --git a/pgd/pgd_search/segment.py b/pgd_search/segment.py similarity index 100% rename from pgd/pgd_search/segment.py rename to pgd_search/segment.py diff --git a/pgd/pgd_search/statistics/__init__.py b/pgd_search/statistics/__init__.py similarity index 100% rename from pgd/pgd_search/statistics/__init__.py rename to pgd_search/statistics/__init__.py diff --git a/pgd/pgd_search/statistics/aggregates.py b/pgd_search/statistics/aggregates.py similarity index 100% rename from pgd/pgd_search/statistics/aggregates.py rename to pgd_search/statistics/aggregates.py diff --git a/pgd/pgd_search/statistics/directional_stddev.py b/pgd_search/statistics/directional_stddev.py similarity index 100% rename from pgd/pgd_search/statistics/directional_stddev.py rename to pgd_search/statistics/directional_stddev.py diff --git a/pgd/pgd_search/statistics/form.py b/pgd_search/statistics/form.py similarity index 100% rename from pgd/pgd_search/statistics/form.py rename to pgd_search/statistics/form.py diff --git a/pgd/pgd_search/statistics/views.py b/pgd_search/statistics/views.py similarity index 100% rename from pgd/pgd_search/statistics/views.py rename to pgd_search/statistics/views.py diff --git a/pgd/pgd_search/templates/angles_helper.html b/pgd_search/templates/angles_helper.html similarity index 100% rename from pgd/pgd_search/templates/angles_helper.html rename to pgd_search/templates/angles_helper.html diff --git a/pgd/pgd_search/templates/browse.html b/pgd_search/templates/browse.html similarity index 100% rename from pgd/pgd_search/templates/browse.html rename to pgd_search/templates/browse.html diff --git a/pgd/pgd_search/templates/chi_help.html b/pgd_search/templates/chi_help.html similarity index 100% rename from pgd/pgd_search/templates/chi_help.html rename to pgd_search/templates/chi_help.html diff --git a/pgd/pgd_search/templates/graph.html b/pgd_search/templates/graph.html similarity index 100% rename from pgd/pgd_search/templates/graph.html rename to pgd_search/templates/graph.html diff --git a/pgd/pgd_search/templates/help.html b/pgd_search/templates/help.html similarity index 100% rename from pgd/pgd_search/templates/help.html rename to pgd_search/templates/help.html diff --git a/pgd/pgd_search/templates/qtiphelp.html b/pgd_search/templates/qtiphelp.html similarity index 100% rename from pgd/pgd_search/templates/qtiphelp.html rename to pgd_search/templates/qtiphelp.html diff --git a/pgd/pgd_search/templates/saveSearch.html b/pgd_search/templates/saveSearch.html similarity index 100% rename from pgd/pgd_search/templates/saveSearch.html rename to pgd_search/templates/saveSearch.html diff --git a/pgd/pgd_search/templates/saved.html b/pgd_search/templates/saved.html similarity index 100% rename from pgd/pgd_search/templates/saved.html rename to pgd_search/templates/saved.html diff --git a/pgd/pgd_search/templates/search.html b/pgd_search/templates/search.html similarity index 100% rename from pgd/pgd_search/templates/search.html rename to pgd_search/templates/search.html diff --git a/pgd/pgd_search/templates/search_base.html b/pgd_search/templates/search_base.html similarity index 100% rename from pgd/pgd_search/templates/search_base.html rename to pgd_search/templates/search_base.html diff --git a/pgd/pgd_search/templates/stats.html b/pgd_search/templates/stats.html similarity index 100% rename from pgd/pgd_search/templates/stats.html rename to pgd_search/templates/stats.html diff --git a/pgd/pgd_search/templates/stats_a.html b/pgd_search/templates/stats_a.html similarity index 100% rename from pgd/pgd_search/templates/stats_a.html rename to pgd_search/templates/stats_a.html diff --git a/pgd/pgd_search/templatetags/__init__.py b/pgd_search/templatetags/__init__.py similarity index 100% rename from pgd/pgd_search/templatetags/__init__.py rename to pgd_search/templatetags/__init__.py diff --git a/pgd/pgd_search/templatetags/search_extras.py b/pgd_search/templatetags/search_extras.py similarity index 100% rename from pgd/pgd_search/templatetags/search_extras.py rename to pgd_search/templatetags/search_extras.py diff --git a/pgd/pgd_search/tester.py b/pgd_search/tester.py similarity index 100% rename from pgd/pgd_search/tester.py rename to pgd_search/tester.py diff --git a/pgd/pgd_search/tests.py b/pgd_search/tests.py similarity index 100% rename from pgd/pgd_search/tests.py rename to pgd_search/tests.py diff --git a/pgd/pgd_search/urls.py b/pgd_search/urls.py similarity index 100% rename from pgd/pgd_search/urls.py rename to pgd_search/urls.py diff --git a/pgd/pgd_search/views.py b/pgd_search/views.py similarity index 100% rename from pgd/pgd_search/views.py rename to pgd_search/views.py diff --git a/pgd/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py similarity index 100% rename from pgd/pgd_splicer/ProcessPDBTask.py rename to pgd_splicer/ProcessPDBTask.py diff --git a/pgd/pgd_splicer/SegmentBuilder.py b/pgd_splicer/SegmentBuilder.py similarity index 100% rename from pgd/pgd_splicer/SegmentBuilder.py rename to pgd_splicer/SegmentBuilder.py diff --git a/pgd/pgd_splicer/__init__.py b/pgd_splicer/__init__.py similarity index 100% rename from pgd/pgd_splicer/__init__.py rename to pgd_splicer/__init__.py diff --git a/pgd/pgd_splicer/chi.py b/pgd_splicer/chi.py similarity index 100% rename from pgd/pgd_splicer/chi.py rename to pgd_splicer/chi.py diff --git a/pgd/pgd_splicer/compareDump.py b/pgd_splicer/compareDump.py similarity index 100% rename from pgd/pgd_splicer/compareDump.py rename to pgd_splicer/compareDump.py diff --git a/pgd/pgd_splicer/compare_protein_dump.py b/pgd_splicer/compare_protein_dump.py similarity index 100% rename from pgd/pgd_splicer/compare_protein_dump.py rename to pgd_splicer/compare_protein_dump.py diff --git a/pgd/pgd_splicer/dbConvert.sql b/pgd_splicer/dbConvert.sql similarity index 100% rename from pgd/pgd_splicer/dbConvert.sql rename to pgd_splicer/dbConvert.sql diff --git a/pgd/pgd_splicer/dump_protein.py b/pgd_splicer/dump_protein.py similarity index 100% rename from pgd/pgd_splicer/dump_protein.py rename to pgd_splicer/dump_protein.py diff --git a/pgd/pgd_splicer/dunbrack_selector.py b/pgd_splicer/dunbrack_selector.py similarity index 100% rename from pgd/pgd_splicer/dunbrack_selector.py rename to pgd_splicer/dunbrack_selector.py diff --git a/pgd/pgd_splicer/fixtures/pgd_core.json b/pgd_splicer/fixtures/pgd_core.json similarity index 100% rename from pgd/pgd_splicer/fixtures/pgd_core.json rename to pgd_splicer/fixtures/pgd_core.json diff --git a/pgd/pgd_splicer/ftpupdate.py b/pgd_splicer/ftpupdate.py similarity index 100% rename from pgd/pgd_splicer/ftpupdate.py rename to pgd_splicer/ftpupdate.py diff --git a/pgd/pgd_splicer/management/__init__.py b/pgd_splicer/management/__init__.py similarity index 100% rename from pgd/pgd_splicer/management/__init__.py rename to pgd_splicer/management/__init__.py diff --git a/pgd/pgd_splicer/management/commands/__init__.py b/pgd_splicer/management/commands/__init__.py similarity index 100% rename from pgd/pgd_splicer/management/commands/__init__.py rename to pgd_splicer/management/commands/__init__.py diff --git a/pgd/pgd_splicer/management/commands/crosscheck.py b/pgd_splicer/management/commands/crosscheck.py similarity index 100% rename from pgd/pgd_splicer/management/commands/crosscheck.py rename to pgd_splicer/management/commands/crosscheck.py diff --git a/pgd/pgd_splicer/management/commands/fetch.py b/pgd_splicer/management/commands/fetch.py similarity index 100% rename from pgd/pgd_splicer/management/commands/fetch.py rename to pgd_splicer/management/commands/fetch.py diff --git a/pgd/pgd_splicer/models.py b/pgd_splicer/models.py similarity index 100% rename from pgd/pgd_splicer/models.py rename to pgd_splicer/models.py diff --git a/pgd/pgd_splicer/process_protein.py b/pgd_splicer/process_protein.py similarity index 100% rename from pgd/pgd_splicer/process_protein.py rename to pgd_splicer/process_protein.py diff --git a/pgd/pgd_splicer/selector.py b/pgd_splicer/selector.py similarity index 100% rename from pgd/pgd_splicer/selector.py rename to pgd_splicer/selector.py diff --git a/pgd/pgd_splicer/sidechain.py b/pgd_splicer/sidechain.py similarity index 100% rename from pgd/pgd_splicer/sidechain.py rename to pgd_splicer/sidechain.py diff --git a/pgd/pgd_splicer/splicer.py b/pgd_splicer/splicer.py similarity index 100% rename from pgd/pgd_splicer/splicer.py rename to pgd_splicer/splicer.py diff --git a/pgd/pgd_splicer/testfiles/cullpdb_crosscheck.txt b/pgd_splicer/testfiles/cullpdb_crosscheck.txt similarity index 100% rename from pgd/pgd_splicer/testfiles/cullpdb_crosscheck.txt rename to pgd_splicer/testfiles/cullpdb_crosscheck.txt diff --git a/pgd/pgd_splicer/testfiles/cullpdb_pc25.gz b/pgd_splicer/testfiles/cullpdb_pc25.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/cullpdb_pc25.gz rename to pgd_splicer/testfiles/cullpdb_pc25.gz diff --git a/pgd/pgd_splicer/testfiles/cullpdb_pc90.gz b/pgd_splicer/testfiles/cullpdb_pc90.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/cullpdb_pc90.gz rename to pgd_splicer/testfiles/cullpdb_pc90.gz diff --git a/pgd/pgd_splicer/testfiles/cullpdb_selection.txt b/pgd_splicer/testfiles/cullpdb_selection.txt similarity index 100% rename from pgd/pgd_splicer/testfiles/cullpdb_selection.txt rename to pgd_splicer/testfiles/cullpdb_selection.txt diff --git a/pgd/pgd_splicer/testfiles/fixture_crosscheck.txt b/pgd_splicer/testfiles/fixture_crosscheck.txt similarity index 100% rename from pgd/pgd_splicer/testfiles/fixture_crosscheck.txt rename to pgd_splicer/testfiles/fixture_crosscheck.txt diff --git a/pgd/pgd_splicer/testfiles/fixture_selection.txt b/pgd_splicer/testfiles/fixture_selection.txt similarity index 100% rename from pgd/pgd_splicer/testfiles/fixture_selection.txt rename to pgd_splicer/testfiles/fixture_selection.txt diff --git a/pgd/pgd_splicer/testfiles/pdb1mwq.ent.gz b/pgd_splicer/testfiles/pdb1mwq.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb1mwq.ent.gz rename to pgd_splicer/testfiles/pdb1mwq.ent.gz diff --git a/pgd/pgd_splicer/testfiles/pdb1mww.ent.gz b/pgd_splicer/testfiles/pdb1mww.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb1mww.ent.gz rename to pgd_splicer/testfiles/pdb1mww.ent.gz diff --git a/pgd/pgd_splicer/testfiles/pdb1twf.ent.gz b/pgd_splicer/testfiles/pdb1twf.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb1twf.ent.gz rename to pgd_splicer/testfiles/pdb1twf.ent.gz diff --git a/pgd/pgd_splicer/testfiles/pdb3cgm.ent.gz b/pgd_splicer/testfiles/pdb3cgm.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb3cgm.ent.gz rename to pgd_splicer/testfiles/pdb3cgm.ent.gz diff --git a/pgd/pgd_splicer/testfiles/pdb3cgx.ent.gz b/pgd_splicer/testfiles/pdb3cgx.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb3cgx.ent.gz rename to pgd_splicer/testfiles/pdb3cgx.ent.gz diff --git a/pgd/pgd_splicer/testfiles/pdb3cgz.ent.gz b/pgd_splicer/testfiles/pdb3cgz.ent.gz similarity index 100% rename from pgd/pgd_splicer/testfiles/pdb3cgz.ent.gz rename to pgd_splicer/testfiles/pdb3cgz.ent.gz diff --git a/pgd/pgd_splicer/testfiles/selection_page.txt b/pgd_splicer/testfiles/selection_page.txt similarity index 100% rename from pgd/pgd_splicer/testfiles/selection_page.txt rename to pgd_splicer/testfiles/selection_page.txt diff --git a/pgd/pgd_splicer/tests.py b/pgd_splicer/tests.py similarity index 100% rename from pgd/pgd_splicer/tests.py rename to pgd_splicer/tests.py diff --git a/pgd/requirements.txt b/requirements.txt similarity index 100% rename from pgd/requirements.txt rename to requirements.txt diff --git a/pgd/settings.py.dist b/settings.py.dist similarity index 100% rename from pgd/settings.py.dist rename to settings.py.dist diff --git a/pgd/spliceTest.py b/spliceTest.py similarity index 100% rename from pgd/spliceTest.py rename to spliceTest.py diff --git a/pgd/static/css/jquery.autocomplete.css b/static/css/jquery.autocomplete.css similarity index 100% rename from pgd/static/css/jquery.autocomplete.css rename to static/css/jquery.autocomplete.css diff --git a/pgd/static/css/jquery.svg.css b/static/css/jquery.svg.css similarity index 100% rename from pgd/static/css/jquery.svg.css rename to static/css/jquery.svg.css diff --git a/pgd/static/css/main.css b/static/css/main.css similarity index 100% rename from pgd/static/css/main.css rename to static/css/main.css diff --git a/pgd/static/css/search.css b/static/css/search.css similarity index 100% rename from pgd/static/css/search.css rename to static/css/search.css diff --git a/pgd/static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png b/static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png similarity index 100% rename from pgd/static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png rename to static/images/Plot-of-phi-vrs-psi-of-the-average-N-CA-C-angle.png diff --git a/pgd/static/images/ajax-error.png b/static/images/ajax-error.png similarity index 100% rename from pgd/static/images/ajax-error.png rename to static/images/ajax-error.png diff --git a/pgd/static/images/ajax-loader.gif b/static/images/ajax-loader.gif similarity index 100% rename from pgd/static/images/ajax-loader.gif rename to static/images/ajax-loader.gif diff --git a/pgd/static/images/delete.png b/static/images/delete.png similarity index 100% rename from pgd/static/images/delete.png rename to static/images/delete.png diff --git a/pgd/static/images/edit.png b/static/images/edit.png similarity index 100% rename from pgd/static/images/edit.png rename to static/images/edit.png diff --git a/pgd/static/images/help/angle_C-1_N_Ca.png b/static/images/help/angle_C-1_N_Ca.png similarity index 100% rename from pgd/static/images/help/angle_C-1_N_Ca.png rename to static/images/help/angle_C-1_N_Ca.png diff --git a/pgd/static/images/help/angle_Ca_C_N+1.png b/static/images/help/angle_Ca_C_N+1.png similarity index 100% rename from pgd/static/images/help/angle_Ca_C_N+1.png rename to static/images/help/angle_Ca_C_N+1.png diff --git a/pgd/static/images/help/angle_Ca_C_O.png b/static/images/help/angle_Ca_C_O.png similarity index 100% rename from pgd/static/images/help/angle_Ca_C_O.png rename to static/images/help/angle_Ca_C_O.png diff --git a/pgd/static/images/help/angle_Cb_Ca_C.png b/static/images/help/angle_Cb_Ca_C.png similarity index 100% rename from pgd/static/images/help/angle_Cb_Ca_C.png rename to static/images/help/angle_Cb_Ca_C.png diff --git a/pgd/static/images/help/angle_N_Ca_C.png b/static/images/help/angle_N_Ca_C.png similarity index 100% rename from pgd/static/images/help/angle_N_Ca_C.png rename to static/images/help/angle_N_Ca_C.png diff --git a/pgd/static/images/help/angle_N_Ca_Cb.png b/static/images/help/angle_N_Ca_Cb.png similarity index 100% rename from pgd/static/images/help/angle_N_Ca_Cb.png rename to static/images/help/angle_N_Ca_Cb.png diff --git a/pgd/static/images/help/angle_O_C_N+1.png b/static/images/help/angle_O_C_N+1.png similarity index 100% rename from pgd/static/images/help/angle_O_C_N+1.png rename to static/images/help/angle_O_C_N+1.png diff --git a/pgd/static/images/help/length_C-1_N.png b/static/images/help/length_C-1_N.png similarity index 100% rename from pgd/static/images/help/length_C-1_N.png rename to static/images/help/length_C-1_N.png diff --git a/pgd/static/images/help/length_C_N+1.png b/static/images/help/length_C_N+1.png similarity index 100% rename from pgd/static/images/help/length_C_N+1.png rename to static/images/help/length_C_N+1.png diff --git a/pgd/static/images/help/length_C_O.png b/static/images/help/length_C_O.png similarity index 100% rename from pgd/static/images/help/length_C_O.png rename to static/images/help/length_C_O.png diff --git a/pgd/static/images/help/length_Ca_C.png b/static/images/help/length_Ca_C.png similarity index 100% rename from pgd/static/images/help/length_Ca_C.png rename to static/images/help/length_Ca_C.png diff --git a/pgd/static/images/help/length_Ca_Cb.png b/static/images/help/length_Ca_Cb.png similarity index 100% rename from pgd/static/images/help/length_Ca_Cb.png rename to static/images/help/length_Ca_Cb.png diff --git a/pgd/static/images/help/length_N-Ca.png b/static/images/help/length_N-Ca.png similarity index 100% rename from pgd/static/images/help/length_N-Ca.png rename to static/images/help/length_N-Ca.png diff --git a/pgd/static/images/help/ome.png b/static/images/help/ome.png similarity index 100% rename from pgd/static/images/help/ome.png rename to static/images/help/ome.png diff --git a/pgd/static/images/help/phi.png b/static/images/help/phi.png similarity index 100% rename from pgd/static/images/help/phi.png rename to static/images/help/phi.png diff --git a/pgd/static/images/help/psi.png b/static/images/help/psi.png similarity index 100% rename from pgd/static/images/help/psi.png rename to static/images/help/psi.png diff --git a/pgd/static/images/help/segment.png b/static/images/help/segment.png similarity index 100% rename from pgd/static/images/help/segment.png rename to static/images/help/segment.png diff --git a/pgd/static/images/help/zeta.png b/static/images/help/zeta.png similarity index 100% rename from pgd/static/images/help/zeta.png rename to static/images/help/zeta.png diff --git a/pgd/static/images/icons/eds.ico b/static/images/icons/eds.ico similarity index 100% rename from pgd/static/images/icons/eds.ico rename to static/images/icons/eds.ico diff --git a/pgd/static/images/icons/pdb.ico b/static/images/icons/pdb.ico similarity index 100% rename from pgd/static/images/icons/pdb.ico rename to static/images/icons/pdb.ico diff --git a/pgd/static/images/icons/pdbsum.ico b/static/images/icons/pdbsum.ico similarity index 100% rename from pgd/static/images/icons/pdbsum.ico rename to static/images/icons/pdbsum.ico diff --git a/pgd/static/images/linear-groups-ramach.png b/static/images/linear-groups-ramach.png similarity index 100% rename from pgd/static/images/linear-groups-ramach.png rename to static/images/linear-groups-ramach.png diff --git a/pgd/static/images/minus2.png b/static/images/minus2.png similarity index 100% rename from pgd/static/images/minus2.png rename to static/images/minus2.png diff --git a/pgd/static/images/monopeptide-inkscape.png b/static/images/monopeptide-inkscape.png similarity index 100% rename from pgd/static/images/monopeptide-inkscape.png rename to static/images/monopeptide-inkscape.png diff --git a/pgd/static/images/plus2.png b/static/images/plus2.png similarity index 100% rename from pgd/static/images/plus2.png rename to static/images/plus2.png diff --git a/pgd/static/js/DejaVu_400.font.full.js b/static/js/DejaVu_400.font.full.js similarity index 100% rename from pgd/static/js/DejaVu_400.font.full.js rename to static/js/DejaVu_400.font.full.js diff --git a/pgd/static/js/DejaVu_400.font.js b/static/js/DejaVu_400.font.js similarity index 100% rename from pgd/static/js/DejaVu_400.font.js rename to static/js/DejaVu_400.font.js diff --git a/pgd/static/js/jqbrowser.js b/static/js/jqbrowser.js similarity index 100% rename from pgd/static/js/jqbrowser.js rename to static/js/jqbrowser.js diff --git a/pgd/static/js/jquery-1.2.6.js b/static/js/jquery-1.2.6.js similarity index 100% rename from pgd/static/js/jquery-1.2.6.js rename to static/js/jquery-1.2.6.js diff --git a/pgd/static/js/jquery-1.3.2.js b/static/js/jquery-1.3.2.js similarity index 100% rename from pgd/static/js/jquery-1.3.2.js rename to static/js/jquery-1.3.2.js diff --git a/pgd/static/js/jquery-1.4.1.min.js b/static/js/jquery-1.4.1.min.js similarity index 100% rename from pgd/static/js/jquery-1.4.1.min.js rename to static/js/jquery-1.4.1.min.js diff --git a/pgd/static/js/jquery.acts_as_tree_table.js b/static/js/jquery.acts_as_tree_table.js similarity index 100% rename from pgd/static/js/jquery.acts_as_tree_table.js rename to static/js/jquery.acts_as_tree_table.js diff --git a/pgd/static/js/jquery.autocomplete.pack.js b/static/js/jquery.autocomplete.pack.js similarity index 100% rename from pgd/static/js/jquery.autocomplete.pack.js rename to static/js/jquery.autocomplete.pack.js diff --git a/pgd/static/js/jquery.progressbar.js b/static/js/jquery.progressbar.js similarity index 100% rename from pgd/static/js/jquery.progressbar.js rename to static/js/jquery.progressbar.js diff --git a/pgd/static/js/jquery.qtip-1.0.0-rc3.min.js b/static/js/jquery.qtip-1.0.0-rc3.min.js similarity index 100% rename from pgd/static/js/jquery.qtip-1.0.0-rc3.min.js rename to static/js/jquery.qtip-1.0.0-rc3.min.js diff --git a/pgd/static/js/jquery.stylish-select.js b/static/js/jquery.stylish-select.js similarity index 100% rename from pgd/static/js/jquery.stylish-select.js rename to static/js/jquery.stylish-select.js diff --git a/pgd/static/js/jquery.svg.compat-1.0.1.js b/static/js/jquery.svg.compat-1.0.1.js similarity index 100% rename from pgd/static/js/jquery.svg.compat-1.0.1.js rename to static/js/jquery.svg.compat-1.0.1.js diff --git a/pgd/static/js/jquery.svg.js b/static/js/jquery.svg.js similarity index 100% rename from pgd/static/js/jquery.svg.js rename to static/js/jquery.svg.js diff --git a/pgd/static/js/jquery.svg.min.js b/static/js/jquery.svg.min.js similarity index 100% rename from pgd/static/js/jquery.svg.min.js rename to static/js/jquery.svg.min.js diff --git a/pgd/static/js/jquery.svg.pack.js b/static/js/jquery.svg.pack.js similarity index 100% rename from pgd/static/js/jquery.svg.pack.js rename to static/js/jquery.svg.pack.js diff --git a/pgd/static/js/jquery.svganim.js b/static/js/jquery.svganim.js similarity index 100% rename from pgd/static/js/jquery.svganim.js rename to static/js/jquery.svganim.js diff --git a/pgd/static/js/jquery.svganim.min.js b/static/js/jquery.svganim.min.js similarity index 100% rename from pgd/static/js/jquery.svganim.min.js rename to static/js/jquery.svganim.min.js diff --git a/pgd/static/js/jquery.svganim.pack.js b/static/js/jquery.svganim.pack.js similarity index 100% rename from pgd/static/js/jquery.svganim.pack.js rename to static/js/jquery.svganim.pack.js diff --git a/pgd/static/js/jquery.svgfilter.js b/static/js/jquery.svgfilter.js similarity index 100% rename from pgd/static/js/jquery.svgfilter.js rename to static/js/jquery.svgfilter.js diff --git a/pgd/static/js/jquery.svgfilter.min.js b/static/js/jquery.svgfilter.min.js similarity index 100% rename from pgd/static/js/jquery.svgfilter.min.js rename to static/js/jquery.svgfilter.min.js diff --git a/pgd/static/js/jquery.svgfilter.pack.js b/static/js/jquery.svgfilter.pack.js similarity index 100% rename from pgd/static/js/jquery.svgfilter.pack.js rename to static/js/jquery.svgfilter.pack.js diff --git a/pgd/static/js/jquery.svggraph.js b/static/js/jquery.svggraph.js similarity index 100% rename from pgd/static/js/jquery.svggraph.js rename to static/js/jquery.svggraph.js diff --git a/pgd/static/js/jquery.svggraph.min.js b/static/js/jquery.svggraph.min.js similarity index 100% rename from pgd/static/js/jquery.svggraph.min.js rename to static/js/jquery.svggraph.min.js diff --git a/pgd/static/js/jquery.svggraph.pack.js b/static/js/jquery.svggraph.pack.js similarity index 100% rename from pgd/static/js/jquery.svggraph.pack.js rename to static/js/jquery.svggraph.pack.js diff --git a/pgd/static/js/pgd.svg.js b/static/js/pgd.svg.js similarity index 100% rename from pgd/static/js/pgd.svg.js rename to static/js/pgd.svg.js diff --git a/pgd/static/js/raphael-min.js b/static/js/raphael-min.js similarity index 100% rename from pgd/static/js/raphael-min.js rename to static/js/raphael-min.js diff --git a/pgd/static/js/search.js b/static/js/search.js similarity index 100% rename from pgd/static/js/search.js rename to static/js/search.js diff --git a/pgd/static/pdf/2009_berkholz.pdf b/static/pdf/2009_berkholz.pdf similarity index 100% rename from pgd/static/pdf/2009_berkholz.pdf rename to static/pdf/2009_berkholz.pdf diff --git a/pgd/static/pdf/2009_berkholz_perspective.pdf b/static/pdf/2009_berkholz_perspective.pdf similarity index 100% rename from pgd/static/pdf/2009_berkholz_perspective.pdf rename to static/pdf/2009_berkholz_perspective.pdf diff --git a/pgd/static/pdf/Berkholz-PGD-2010.pdf b/static/pdf/Berkholz-PGD-2010.pdf similarity index 100% rename from pgd/static/pdf/Berkholz-PGD-2010.pdf rename to static/pdf/Berkholz-PGD-2010.pdf diff --git a/pgd/static/pdf/Hollingsworth-etal_2009.pdf b/static/pdf/Hollingsworth-etal_2009.pdf similarity index 100% rename from pgd/static/pdf/Hollingsworth-etal_2009.pdf rename to static/pdf/Hollingsworth-etal_2009.pdf diff --git a/pgd/static/pdf/Karplus_2008_letter.pdf b/static/pdf/Karplus_2008_letter.pdf similarity index 100% rename from pgd/static/pdf/Karplus_2008_letter.pdf rename to static/pdf/Karplus_2008_letter.pdf diff --git a/pgd/templates/404.html b/templates/404.html similarity index 100% rename from pgd/templates/404.html rename to templates/404.html diff --git a/pgd/templates/500.html b/templates/500.html similarity index 100% rename from pgd/templates/500.html rename to templates/500.html diff --git a/pgd/templates/base.html b/templates/base.html similarity index 100% rename from pgd/templates/base.html rename to templates/base.html diff --git a/pgd/templates/contactus.html b/templates/contactus.html similarity index 100% rename from pgd/templates/contactus.html rename to templates/contactus.html diff --git a/pgd/templates/news.html b/templates/news.html similarity index 100% rename from pgd/templates/news.html rename to templates/news.html diff --git a/pgd/templates/references.html b/templates/references.html similarity index 100% rename from pgd/templates/references.html rename to templates/references.html diff --git a/pgd/templates/registration/activate.html b/templates/registration/activate.html similarity index 100% rename from pgd/templates/registration/activate.html rename to templates/registration/activate.html diff --git a/pgd/templates/registration/activation_email.txt b/templates/registration/activation_email.txt similarity index 100% rename from pgd/templates/registration/activation_email.txt rename to templates/registration/activation_email.txt diff --git a/pgd/templates/registration/activation_email_subject.txt b/templates/registration/activation_email_subject.txt similarity index 100% rename from pgd/templates/registration/activation_email_subject.txt rename to templates/registration/activation_email_subject.txt diff --git a/pgd/templates/registration/login.html b/templates/registration/login.html similarity index 100% rename from pgd/templates/registration/login.html rename to templates/registration/login.html diff --git a/pgd/templates/registration/logout.html b/templates/registration/logout.html similarity index 100% rename from pgd/templates/registration/logout.html rename to templates/registration/logout.html diff --git a/pgd/templates/registration/password_change_done.html b/templates/registration/password_change_done.html similarity index 100% rename from pgd/templates/registration/password_change_done.html rename to templates/registration/password_change_done.html diff --git a/pgd/templates/registration/password_change_form.html b/templates/registration/password_change_form.html similarity index 100% rename from pgd/templates/registration/password_change_form.html rename to templates/registration/password_change_form.html diff --git a/pgd/templates/registration/password_reset_complete.html b/templates/registration/password_reset_complete.html similarity index 100% rename from pgd/templates/registration/password_reset_complete.html rename to templates/registration/password_reset_complete.html diff --git a/pgd/templates/registration/password_reset_confirm.html b/templates/registration/password_reset_confirm.html similarity index 100% rename from pgd/templates/registration/password_reset_confirm.html rename to templates/registration/password_reset_confirm.html diff --git a/pgd/templates/registration/password_reset_done.html b/templates/registration/password_reset_done.html similarity index 100% rename from pgd/templates/registration/password_reset_done.html rename to templates/registration/password_reset_done.html diff --git a/pgd/templates/registration/password_reset_email.html b/templates/registration/password_reset_email.html similarity index 100% rename from pgd/templates/registration/password_reset_email.html rename to templates/registration/password_reset_email.html diff --git a/pgd/templates/registration/password_reset_form.html b/templates/registration/password_reset_form.html similarity index 100% rename from pgd/templates/registration/password_reset_form.html rename to templates/registration/password_reset_form.html diff --git a/pgd/templates/registration/registration_complete.html b/templates/registration/registration_complete.html similarity index 100% rename from pgd/templates/registration/registration_complete.html rename to templates/registration/registration_complete.html diff --git a/pgd/templates/registration/registration_form.html b/templates/registration/registration_form.html similarity index 100% rename from pgd/templates/registration/registration_form.html rename to templates/registration/registration_form.html diff --git a/pgd/templates/welcome.html b/templates/welcome.html similarity index 100% rename from pgd/templates/welcome.html rename to templates/welcome.html diff --git a/pgd/urls.py b/urls.py similarity index 100% rename from pgd/urls.py rename to urls.py From 3675d828c2cb6a12ac15dc165582d877197c8091 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Thu, 6 Nov 2014 10:16:24 -0800 Subject: [PATCH 19/28] Popped keys instead of deleting them. If a residue has chi corrections and both atoms in the dictionary are absent, the code attempts to delete non-existent keys. The code has been changed to pop the keys, which will prevent the KeyError from being thrown. Refs #17229 --- pgd_splicer/ProcessPDBTask.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py index d5825ea..d75ece8 100755 --- a/pgd_splicer/ProcessPDBTask.py +++ b/pgd_splicer/ProcessPDBTask.py @@ -608,11 +608,13 @@ def parseWithBioPython(path, props, chains_filter=None): if old_atom1: atoms[atom2] = old_atom1 else: - del atoms[atom2] + # del atoms[atom2] + atoms.pop(atom2, None) if old_atom2: atoms[atom1] = old_atom2 else: - del atoms[atom1] + # del atoms[atom1] + atoms.pop(atom1, None) #Calculate CHI values. The mappings for per peptide chi's are stored From 67fb7a2a0388cf7fa0b55924c23e4a34e29a8f56 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Fri, 7 Nov 2014 10:42:53 -0800 Subject: [PATCH 20/28] Any lines with ATOM that contain SEC should be changed to HETATM. The PDB contains files for proteins that treat selenocysteine as an amino acid, but DSSP does not yet support this condition. Temporary files generated for DSSP will now be modified if they have ATOM lines that contain SEC. Refs #17223 --- pgd_splicer/ProcessPDBTask.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py index d5825ea..94c4264 100755 --- a/pgd_splicer/ProcessPDBTask.py +++ b/pgd_splicer/ProcessPDBTask.py @@ -357,6 +357,18 @@ def hetatm_amino(s): return s.startswith("HETATM") and any(amino in s for amino in AA3to1) +def atom_sec(s): + """ + Any lines with ATOM that contain SEC should be changed to HETATM. + + See #17223 for details. + """ + + if s.startswith("ATOM ") and "SEC" in s: + return s.replace("ATOM ", "HETATM") + else: + return s + def parseWithBioPython(path, props, chains_filter=None): """ Parse values from file that can be parsed using BioPython library @@ -376,7 +388,10 @@ def parseWithBioPython(path, props, chains_filter=None): # HETATM pattern. This is largely for 2VQ1, see #8319 for details. for line in gunzipped: if not hetatm_amino(line): - decompressed.write(line) + # SEC is now an amino acid and will appear in ATOM lines. + # Unfortunately, DSSP cannot handle this, so any ATOM lines with + # SEC must be changed to HETATM lines. + decompressed.write(atom_sec(line)) # Be kind; rewind. decompressed.seek(0) From dd55533f25c4e128f374b09c1be87cc350b49901 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Fri, 7 Nov 2014 10:42:53 -0800 Subject: [PATCH 21/28] Any lines with ATOM that contain SEC should be changed to HETATM. The PDB contains files for proteins that treat selenocysteine as an amino acid, but DSSP does not yet support this condition. Temporary files generated for DSSP will now be modified if they have ATOM lines that contain SEC. Refs #17223 --- pgd_splicer/ProcessPDBTask.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py index d75ece8..08ed7ed 100755 --- a/pgd_splicer/ProcessPDBTask.py +++ b/pgd_splicer/ProcessPDBTask.py @@ -357,6 +357,18 @@ def hetatm_amino(s): return s.startswith("HETATM") and any(amino in s for amino in AA3to1) +def atom_sec(s): + """ + Any lines with ATOM that contain SEC should be changed to HETATM. + + See #17223 for details. + """ + + if s.startswith("ATOM ") and "SEC" in s: + return s.replace("ATOM ", "HETATM") + else: + return s + def parseWithBioPython(path, props, chains_filter=None): """ Parse values from file that can be parsed using BioPython library @@ -376,7 +388,10 @@ def parseWithBioPython(path, props, chains_filter=None): # HETATM pattern. This is largely for 2VQ1, see #8319 for details. for line in gunzipped: if not hetatm_amino(line): - decompressed.write(line) + # SEC is now an amino acid and will appear in ATOM lines. + # Unfortunately, DSSP cannot handle this, so any ATOM lines with + # SEC must be changed to HETATM lines. + decompressed.write(atom_sec(line)) # Be kind; rewind. decompressed.seek(0) From d9283240e96d4c87e9657b1a3e65fbd7bd2ce1fa Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Thu, 20 Nov 2014 20:30:06 +0000 Subject: [PATCH 22/28] Modified amino acid checks to include SEC. Selenocysteine is now considered an amino acid by the PDB and DSSP folks, so the "amino acid" checks for HETATM lines have been changed. In addition, a new check for ATOM lines has been added. Refs #17223 --- pgd_splicer/ProcessPDBTask.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py index 08ed7ed..eae3fc5 100755 --- a/pgd_splicer/ProcessPDBTask.py +++ b/pgd_splicer/ProcessPDBTask.py @@ -347,6 +347,18 @@ def pdb_file_is_newer(data): return protein.pdb_date < pdb_date and str(protein.pdb_date) != str(pdb_date)[:19] +def amino_present(s): + """ + Whether a given line contains a valid amino acid. + + NB: "SEC" is a valid amino acid, regardless of its absence from + AA3to1. + + See #17223 for details. + """ + + return any(amino in s for amino in AA3to1) or "SEC" in s + def hetatm_amino(s): """ Whether a given line refers to a HETATM amino acid. @@ -354,20 +366,18 @@ def hetatm_amino(s): These lines are undesirable and we should discard them. """ - return s.startswith("HETATM") and any(amino in s for amino in AA3to1) + return s.startswith("HETATM ") and amino_present(s) -def atom_sec(s): +def atom_noamino(s): """ - Any lines with ATOM that contain SEC should be changed to HETATM. + Whether a given line is an ATOM line with no valid amino acid. - See #17223 for details. + These lines are undesirable and we should discard them. """ - if s.startswith("ATOM ") and "SEC" in s: - return s.replace("ATOM ", "HETATM") - else: - return s + return s.startswith("ATOM ") and not amino_present(s) + def parseWithBioPython(path, props, chains_filter=None): """ @@ -386,12 +396,11 @@ def parseWithBioPython(path, props, chains_filter=None): # Go through, one line at a time, and discard lines that have the bad # HETATM pattern. This is largely for 2VQ1, see #8319 for details. + # Also remove any ATOM lines with invalid amino acids. + # See #17223 for details. for line in gunzipped: - if not hetatm_amino(line): - # SEC is now an amino acid and will appear in ATOM lines. - # Unfortunately, DSSP cannot handle this, so any ATOM lines with - # SEC must be changed to HETATM lines. - decompressed.write(atom_sec(line)) + if not hetatm_amino(line) and not atom_noamino(line): + decompressed.write(line) # Be kind; rewind. decompressed.seek(0) From ec62c84dfe4011c9e0bc3ca43ee4d6300fad0ff6 Mon Sep 17 00:00:00 2001 From: Jack Twilley Date: Sun, 23 Nov 2014 09:46:10 +0000 Subject: [PATCH 23/28] Removed explicit reference to SEC in amino_present The amino_present function explicitly checked for selenocysteine which caused other proteins to not import correctly. Leaving it out makes all the proteins import correctly. Refs #17223. --- pgd_splicer/ProcessPDBTask.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pgd_splicer/ProcessPDBTask.py b/pgd_splicer/ProcessPDBTask.py index eae3fc5..a506621 100755 --- a/pgd_splicer/ProcessPDBTask.py +++ b/pgd_splicer/ProcessPDBTask.py @@ -357,7 +357,7 @@ def amino_present(s): See #17223 for details. """ - return any(amino in s for amino in AA3to1) or "SEC" in s + return any(amino in s for amino in AA3to1) def hetatm_amino(s): """ From d1c5d9592128621ebe1b2fd92dffafa5afda54e3 Mon Sep 17 00:00:00 2001 From: Ian Kronquist Date: Sat, 31 Jan 2015 09:40:23 -0800 Subject: [PATCH 24/28] Move svg.py to minimize redundancies Addresses #17799 --- pgd_search/plot/svg.py | 125 ------------------------------ pgd_search/{histogram => }/svg.py | 0 2 files changed, 125 deletions(-) delete mode 100644 pgd_search/plot/svg.py rename pgd_search/{histogram => }/svg.py (100%) diff --git a/pgd_search/plot/svg.py b/pgd_search/plot/svg.py deleted file mode 100644 index 2d556f5..0000000 --- a/pgd_search/plot/svg.py +++ /dev/null @@ -1,125 +0,0 @@ -from math import radians - -import cairocffi as cairo - - -""" Set of classes for helping deal with SVG graphics """ - - -class SVG(): - """ - Class is a container for basic drawing objects. It is used to record - actions that make up an svg so that they may be redrawn using Jquery, pycairo - or another drawing tool - """ - - def __init__(self): - self.operations = [] - - def line(self,x,y,x1,y1,stroke=1,color='black'): - self.operations.append(Line(x,y,x1,y1,stroke,color)) - - def rect(self, x,y,height,width,stroke=1,color='black',fill=None,data=None): - self.operations.append(Rect(x,y,height,width,stroke,color, fill, data)) - - def text(self, x,y,text, size=16,fontfamily='Verdana', fill='black', rotate=0): - self.operations.append( Text(x,y,text,size,fontfamily, fill, rotate)) - - def to_dict(self): - """ - Convert object to dictionary so that it may be json serialized - """ - return [op.__dict__ for op in self.operations] - - def render_png(self, writer, width, height): - """ - Renders this svg object as a PNG - @param writer - any file like object that has a write method - @param width - width of the png - @param height - height of the png - """ - - surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, width, height) - context = cairo.Context (surface) - - for op in self.operations: - if op.type == 'line': - context.move_to(op.x+.5, op.y+.5) - context.line_to(op.x1+.5, op.y1+.5) - context.set_line_width(op.stroke) - r,g,b = RGBTuple(op.color) - context.set_source_rgba(r,g,b,1) - context.stroke() - - elif op.type == 'text': - red, green, blue = RGBTuple(op.color) - context.set_source_rgba(red,green,blue,1) - context.set_font_size (op.size) - context.move_to (op.x, op.y) - context.rotate(radians(op.rotate)) - context.show_text (op.text) - context.rotate(0) - - elif op.type == 'rect': - context.rectangle(op.x, op.y, op.width, op.height) - if op.color and op.color <> 'None': - red, green, blue = RGBTuple(op.color) - context.set_source_rgba(red,green,blue,1) - context.set_line_width(op.stroke) - context.stroke_preserve() - if op.fill and op.fill <> 'None': - r,g,b = RGBTuple(op.fill) - context.set_source_rgba(r,g,b,1) - else: - context.set_source_rgba(0,0,0,0) - context.fill() - - surface.write_to_png(writer) - - - -class Line(): - def __init__(self, x,y,x1,y1,stroke=1,color='black'): - self.type = 'line' - self.x = x - self.y = y - self.x1 = x1 - self.y1 = y1 - self.stroke = stroke - self.color = color - - -class Rect(): - def __init__(self,x,y,height,width,stroke=0,color='black',fill=None,data=None): - self.type = 'rect' - self.x = x - self.y = y - self.width = width - self.height = height - self.stroke = stroke - self.color = color - self.fill = fill - self.data = data - - -class Text(): - def __init__(self, x,y,text, size,color='#000000', fontfamily='Verdana', rotate=0): - self.type = 'text' - self.x = x - self.y = y - self.text = text - self.size = size - self.fontfamily = fontfamily - self.color = color - self.rotate = rotate - - -def RGBTuple(rgbString): - """ - Converts a hex string to a tuple of RGB integer values - """ - sub = rgbString[-6:] - red = int(sub[:2],16)/255.0 - green = int(sub[2:4],16)/255.0 - blue = int(sub[4:], 16)/255.0 - return (red,green,blue) diff --git a/pgd_search/histogram/svg.py b/pgd_search/svg.py similarity index 100% rename from pgd_search/histogram/svg.py rename to pgd_search/svg.py From 4e00c3db68ec9a611ed4a20d740520bf4a9397c4 Mon Sep 17 00:00:00 2001 From: Ian Kronquist Date: Sat, 31 Jan 2015 09:41:19 -0800 Subject: [PATCH 25/28] Change svg import path to reflect move --- pgd_search/histogram/Histogram.py | 2 +- pgd_search/plot/ConfDistFuncs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pgd_search/histogram/Histogram.py b/pgd_search/histogram/Histogram.py index a118fe9..cbfa04c 100644 --- a/pgd_search/histogram/Histogram.py +++ b/pgd_search/histogram/Histogram.py @@ -8,7 +8,7 @@ from pgd_constants import * from pgd_core.models import * from pgd_search.models import * -from svg import * +from pgd_search.svg import * from pgd_search.statistics.aggregates import BinSort diff --git a/pgd_search/plot/ConfDistFuncs.py b/pgd_search/plot/ConfDistFuncs.py index 1c6d470..587079e 100644 --- a/pgd_search/plot/ConfDistFuncs.py +++ b/pgd_search/plot/ConfDistFuncs.py @@ -16,7 +16,7 @@ from pgd_search.models import * from pgd_search.statistics.aggregates import DirectionalAvg, DirectionalStdDev, BinSort from pgd_splicer.sidechain import sidechain_length_relationship_list, sidechain_angle_relationship_list -from svg import * +from pgd_search.svg import * ANGLES = ('ome', 'phi', 'psi', 'chi1','chi2','chi3','chi4','chi5','zeta') NON_FIELDS = ('Observations', 'all') From e6b85cc4f3b2fb54199a8d6f847d65d1450cb278 Mon Sep 17 00:00:00 2001 From: Ian Kronquist Date: Sat, 31 Jan 2015 16:31:42 -0800 Subject: [PATCH 26/28] Add python-decouple for environment based config Optional of course. An environment based config makes dockerization much easier. --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 301efe2..da5fa72 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ numpy simplejson cairocffi Sphinx +python-decouple==2.3 From ef3e611b75015af1b13a48d0b0352486b4bf829b Mon Sep 17 00:00:00 2001 From: Ian Kronquist Date: Sat, 31 Jan 2015 16:32:50 -0800 Subject: [PATCH 27/28] Use decouple for an optional environment config By default it falls back to previous values. --- settings.py.dist | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/settings.py.dist b/settings.py.dist index 65ff97d..ee24477 100644 --- a/settings.py.dist +++ b/settings.py.dist @@ -1,7 +1,9 @@ # Django settings for pgd project. +from decouple import config -DEBUG = False + +DEBUG = config('DEBUG', default=False, cast=bool) TEMPLATE_DEBUG = DEBUG ADMINS = ( @@ -10,57 +12,57 @@ ADMINS = ( MANAGERS = ADMINS -DATABASE_ENGINE = 'mysql' # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. -DATABASE_NAME = 'master_naturalist_db' # Or path to database file if sqlite3. -DATABASE_USER = 'root' # Not used with sqlite3. -DATABASE_PASSWORD = 'scott' # Not used with sqlite3. -DATABASE_HOST = '' # Set to empty string for localhost. -DATABASE_PORT = '' # Set to empty string for default. +DATABASE_ENGINE = config('DATABASE_ENGINE', default='mysql') # 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. +DATABASE_NAME = config('DATABASE_NAME', default='master_naturalist_db') # Or path to database file if sqlite3. +DATABASE_USER = config('DATABASE_USER', default='root') # Not used with sqlite3. +DATABASE_PASSWORD = config('DATABASE_PASSWORD', default='scott') # Not used with sqlite3. +DATABASE_HOST = config('DATABASE_HOST', default='') # Set to empty string for localhost. +DATABASE_PORT = config('DATABASE_PORT', default='') # Set to empty string for default. # prefix used for the site. ie. http://myhost.com// # for the django standalone server this should be "" # for apache this is the url the site is mapped to, probably /pgd -SITE_ROOT = '' +SITE_ROOT = config('SITE_ROOT', default='') # absolute path to the docroot of this site -DOC_ROOT = '' +DOC_ROOT = config('DOC_ROOT', default='') # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'America/Chicago' +TIME_ZONE = config('TIME_ZONE', default='America/Chicago') # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html -LANGUAGE_CODE = 'en-us' +LANGUAGE_CODE = config('LANGUAGE_CODE', default='en-us') -SITE_ID = 1 +SITE_ID = config('SITE_ID', default=1, cast=int) #Google analytics ID. Enter the full id, as in: UA-xxxxxx-x -GOOGLE_ID = 'UA-9880071-1' +GOOGLE_ID = config('GOOGLE_ID', default='UA-9880071-1') # If you set this to False, Django will make some optimizations so as not # to load the internationalization machinery. -USE_I18N = True +USE_I18N = config('USE_I18N', default=True, cast=bool) # Absolute path to the directory that holds media. # Example: "/home/media/media.lawrence.com/" -MEDIA_ROOT = '%s/static' % DOC_ROOT +MEDIA_ROOT = config('MEDIA_ROOT', default='%s/static' % DOC_ROOT) # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash if there is a path component (optional in other cases). # Examples: "http://media.lawrence.com", "http://example.com/media/" -MEDIA_URL = '%s/%s' % (SITE_ROOT, 'static') +MEDIA_URL = config('MEDIA_URL', default='%s/%s' % (SITE_ROOT, 'static')) # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a # trailing slash. # Examples: "http://foo.com/media/", "/media/". -ADMIN_MEDIA_PREFIX = '/media/' +ADMIN_MEDIA_PREFIX = config('ADMIN_MEDIA_PREFIX', default='/media/') # Make this unique, and don't share it with anybody. -SECRET_KEY = 'g3#f517j(b4$ze*3_8pi3i$=1@mk=fnbb_gunjwk_f8whrbo=a' +SECRET_KEY = config('SECRET_KEY', default='g3#f517j(b4$ze*3_8pi3i$=1@mk=fnbb_gunjwk_f8whrbo=a') # List of callables that know how to import templates from various sources. TEMPLATE_LOADERS = ( @@ -84,7 +86,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( 'context_processors.PGDContextProcessor', ) -ROOT_URLCONF = 'urls' +ROOT_URLCONF = config('ROOT_URLCONF', default='urls') TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". @@ -105,6 +107,6 @@ INSTALLED_APPS = ( ) # PGD Specific settings -QUERY_LIMIT = 50000000 -SEGMENT_SIZE = 10 -DATA_VERSION = 'testing' +QUERY_LIMIT = config('QUERY_LIMIT', default=50000000, cast=int) +SEGMENT_SIZE = config('SEGMENT_SIZE', default=10, cast=int) +DATA_VERSION = config('DATA_VERSION', default='testing') From 40c9613ac437e7e850a726b699856df84041bce0 Mon Sep 17 00:00:00 2001 From: Ian Kronquist Date: Sat, 31 Jan 2015 16:33:48 -0800 Subject: [PATCH 28/28] =?UTF-8?q?Add=20a=20Dockerfile!=20=F0=9F=90=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..010aa30 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM centos:7 + +MAINTAINER OSU OSL support@osuosl.org + +EXPOSE 8000 + +#Sane configuration defaults +ENV DATABASE_ENGINE mysql +ENV DATABASE_NAME pgd_db +ENV DATABASE_USER root +ENV DATABASE_PASSWORD pgd_db +ENV DATABASE_HOST mysql + +RUN yum install -y mysql mysql-devel gcc gcc-c++ git libcairo-devel cairo pycairo python-setuptools python-devel libffi libffi-devel + +RUN easy_install pip + +# Copy and configure pgd +WORKDIR /opt/pgd +# Copy requirements.txt separately for better caching +COPY ./requirements.txt /opt/pgd/requirements.txt +RUN pip install -r requirements.txt +COPY . /opt/pgd +RUN cp ./settings.py.dist ./settings.py + +CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]