1###############################################################################
2#
3# checkmData.py - database management utilities
4#
5###############################################################################
6#                                                                             #
7#    This program is free software: you can redistribute it and/or modify     #
8#    it under the terms of the GNU General Public License as published by     #
9#    the Free Software Foundation, either version 3 of the License, or        #
10#    (at your option) any later version.                                      #
11#                                                                             #
12#    This program is distributed in the hope that it will be useful,          #
13#    but WITHOUT ANY WARRANTY; without even the implied warranty of           #
14#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            #
15#    GNU General Public License for more details.                             #
16#                                                                             #
17#    You should have received a copy of the GNU General Public License        #
18#    along with this program. If not, see <http://www.gnu.org/licenses/>.     #
19#                                                                             #
20###############################################################################
21
22import os
23import sys
24import logging
25from pkg_resources import resource_filename
26import json
27
28import checkm.manifestManager as mm
29
30
31class DBConfig(object):
32    """CheckM uses packageutils to distribute a file called "DATA_CONFIG" which is placed with
33    the other files during installation. This file stores information about where data is
34    stored locally and where to look for remote updates. This class essentially exposes
35    the DATA_CONFIG file as an object.
36    """
37    def __init__(self):
38        self.logger = logging.getLogger('timestamp')
39        self.configFile = os.path.abspath(resource_filename('checkm', 'DATA_CONFIG'))
40        self.values = self.getConfig()
41
42#-----------------------------------------------------------------------------
43# Read and write local config file
44
45    def getConfig(self):
46        """Get a listing of the versions of files in the local data config"""
47        try:
48            with open(self.configFile, 'r') as local_config:
49                # config is a one line file
50                for line in local_config:
51                    return json.loads(line)
52
53        except Exception:
54            self.logger.error("There seems to be a problem with loading the CheckM config file")
55            self.logger.error("Please check the permissions / existence / contents of:")
56            self.logger.error(self.configFile)
57            raise
58
59        return {}
60
61    def setConfig(self):
62        """Update the local config to reflect and changes made"""
63        if self.checkPermissions():
64            with open(self.configFile, 'w') as config_fh:
65                config_fh.write(json.dumps(self.values))
66
67#-----------------------------------------------------------------------------
68# Display config
69
70    def displayConfig(self):
71        """Print out the contents of the CheckM config file"""
72        self.logger.info("Contents of the config located at:\n\n\t%s\n" % (self.configFile))
73        self.logger.info("-------------------------------------------------------------------------------")
74        self.logger.info("Data root: %s" % self.values["dataRoot"])
75        self.logger.info("Local manifest name: %s" % self.values["remoteManifestURL"])
76        self.logger.info("Manifest type: %s" % self.values["remoteManifestName"])
77        self.logger.info("Remote URL: %s" % self.values["localManifestName"])
78        self.logger.info("Remote manifest name: %s" % self.values["manifestType"])
79        self.logger.info("-------------------------------------------------------------------------------")
80
81#-----------------------------------------------------------------------------
82# Housekeeping
83
84    def checkPermissions(self):
85        """Work out if we have permission to write to the CheckM config before attempting to make changes"""
86        try:
87            open(self.configFile, 'a')
88        except IOError, e:
89            print "You do not seem to have permission to edit the checkm config file"
90            print "located at %s" % self.configFile
91            print "Please try again with updated privileges. Error was:\n"
92            print e
93            return False
94        return True
95
96
97class DBManager(mm.ManifestManager):
98
99    """Manage all aspects of data location and version control."""
100    def __init__(self, set_path=None):
101        mm.ManifestManager.__init__(self, timeout=15)
102        self.logger = logging.getLogger('timestamp')
103        self.config = DBConfig()  # load inbuilt configuration
104        self.type = self.config.values["manifestType"]
105
106        if set_path:
107            self.setRoot(set_path)
108
109        # check that the data root is legit
110        manifestFile = os.path.join(self.config.values["dataRoot"], mm.__MANIFEST__)
111        if not os.path.exists(self.config.values["dataRoot"]) or not os.path.exists(manifestFile):
112            self.config.values["dataRoot"] = ""
113
114        if self.config.values["dataRoot"] == "":
115            # no data folder set.
116            print ("It seems that the CheckM data folder has not been set yet or has been removed. Please run 'checkm data setRoot'.")
117            if not self.setRoot():
118                print("Sorry, CheckM cannot run without a valid data folder.")
119
120    def runAction(self, action):
121        """Main entry point for the updating code"""
122
123        if action[0] == "setRoot":
124            if len(action) > 1:
125                path = self.setRoot(path=action[1])
126            else:
127                path = self.setRoot()
128
129            if path is None:
130                self.logger.info("Data location not changed")
131            else:
132                self.logger.info("Data location successfully changed to: %s" % path)
133        else:
134            self.logger.error("Unknown action: %s" % action[0])
135
136    def setRoot(self, path=None):
137        """Set the data folder"""
138        # check to see we have permission to update the data root
139        if not self.config.checkPermissions():
140            return None
141
142        # validate the supplied path or prompt for a new one
143        path = self.confirmPath(path=path)
144        if path is None:
145            # The user is not interested
146            return None
147
148        # path should be set, exist and be writable
149        self.config.values["dataRoot"] = path
150
151        # save the new path
152        self.config.setConfig()
153
154        return path
155
156    def confirmPath(self, path=None):
157        """Ask the user to supply a path"""
158        path_set = False
159        minimal = False
160        while not path_set:
161            if not path:
162                path = os.path.join(os.path.expanduser("~"), ".checkm")
163
164            if path.upper() == "ABORT":
165                # user has given up
166                return None
167            else:
168                path = os.path.abspath(os.path.expanduser(path))
169
170            print ""
171            if os.path.exists(path):
172                # path exists
173                if os.access(path, os.W_OK):
174                    # path is writable
175                    path_set = True
176                    print "Path [%s] exists and you have permission to write to this folder." % path
177                else:
178                    print "Path [%s] exists but you do not have permission to write to this folder." % path
179            else:
180                # path does not exist, try to make it
181                "Path [%s] does not exist so I will attempt to create it" % path
182                try:
183                    self.makeSurePathExists(path)
184                    print "Path [%s] has been created and you have permission to write to this folder." % path
185                    path_set = True
186                except Exception:
187                    print "Unable to make the folder, Error was: %s" % sys.exc_info()[0]
188                minimal = True
189
190        # (re)make the manifest file
191        print "(re) creating manifest file (please be patient)."
192        self.createManifest(path, self.config.values["localManifestName"])
193
194        return path
195
196    def checkPermissions(self):
197        """See if the user has permission to write to the data directory"""
198        if not os.access(self.config.values["dataRoot"], os.W_OK):
199            print "You do not seem to have permission to edit the CheckM data folder"
200            print "located at %s" % self.config.values["dataRoot"]
201            return False
202
203        return True
204