~thisfred/desktopcouch/the-great-split-of-2010

« back to all changes in this revision

Viewing changes to desktopcouch/records/server.py

  • Committer: Elliot Murphy
  • Date: 2009-07-08 17:48:11 UTC
  • Revision ID: elliot@canonical.com-20090708174811-zfcdtoq8f71djgcd
Initial release, under LGPLv3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright 2009 Canonical Ltd.
 
2
#
 
3
# This file is part of desktopcouch.
 
4
#
 
5
#  desktopcouch is free software: you can redistribute it and/or modify
 
6
# it under the terms of the GNU Lesser General Public License version 3
 
7
# as published by the Free Software Foundation.
 
8
#
 
9
# desktopcouch is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU Lesser General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU Lesser General Public License
 
15
# along with desktopcouch.  If not, see <http://www.gnu.org/licenses/>.
 
16
#
 
17
# Authors: Eric Casteleijn <eric.casteleijn@canonical.com>
 
18
#          Mark G. Saye <mark.saye@canonical.com>
 
19
#          Stuart Langridge <stuart.langridge@canonical.com>
 
20
 
 
21
"""The Desktop Couch Records API."""
 
22
 
 
23
from couchdb import Server
 
24
from couchdb.client import ResourceNotFound
 
25
 
 
26
from record import Record
 
27
 
 
28
class NoSuchDatabase(Exception):
 
29
    "Exception for trying to use a non-existent database"
 
30
 
 
31
    def __init__(self, dbname):
 
32
        self.database = dbname
 
33
        super(NoSuchDatabase, self).__init__()
 
34
 
 
35
    def __str__(self):
 
36
        return ("Database %s does not exist on this server. (Create it by "
 
37
                "passing create=True)") % self.database
 
38
 
 
39
 
 
40
class CouchDatabase(object):
 
41
    """An small records specific abstraction over a couch db database."""
 
42
 
 
43
    def __init__(self, database, uri=None, record_factory=None, create=False):
 
44
        if not uri:
 
45
            port = self._find_couchdb_port()
 
46
            self.server_uri = "http://localhost:%s" % port
 
47
        else:
 
48
            self.server_uri = uri
 
49
        self._server = Server(self.server_uri)
 
50
        if database not in self._server:
 
51
            if create:
 
52
                self._server.create(database)
 
53
            else:
 
54
                raise NoSuchDatabase(database)
 
55
        self.db = self._server[database]
 
56
        self.record_factory = record_factory or Record
 
57
 
 
58
    def _find_couchdb_port(self):
 
59
        """Look for a running Desktop CouchDB. If there isn't one, fall back
 
60
           to the system CouchDB on 5984."""
 
61
        # Currently using horrible lsof command; this will eventually use D-Bus
 
62
        # pylint: disable-msg=W0702
 
63
        try:
 
64
            import subprocess
 
65
            from desktopcouch import local_files
 
66
            lsofcmd = 'lsof -i4TCP@localhost | grep $(couchdb -C %s ' + \
 
67
               '-p %s -s | cut -d" " -f7 | cut -d "," -f1) 2>/dev/null | ' + \
 
68
                      'grep "TCP " | grep LISTEN | ' + \
 
69
                      'awk \'{print $8}\' | cut -d":" -f2'
 
70
            actual_lsof_cmd = lsofcmd % (local_files.FILE_INI, 
 
71
              local_files.FILE_PID)
 
72
            stdout, stderr = subprocess.Popen(actual_lsof_cmd, shell=True,
 
73
              stdout=subprocess.PIPE).communicate()
 
74
            port = stdout.strip()
 
75
        except:
 
76
            port = 5984
 
77
        if not port: 
 
78
            port = 5984
 
79
        return port
 
80
    
 
81
    def query(self, map_fun, reduce_fun=None, language='javascript', 
 
82
      wrapper=None, **options):
 
83
        """Pass-through to CouchDB query"""
 
84
        return self.db.query(map_fun, reduce_fun, language, 
 
85
              wrapper, **options)
 
86
    
 
87
    def get_record(self, record_id):
 
88
        """Get a record from back end storage."""
 
89
        try:
 
90
            couch_record = self.db[record_id]
 
91
        except ResourceNotFound:
 
92
            return None
 
93
        data = {}
 
94
        data.update(couch_record)
 
95
        record = self.record_factory(data=data)
 
96
        record.record_id = record_id
 
97
        return record
 
98
 
 
99
    def put_record(self, record):
 
100
        """Put a record in back end storage."""
 
101
        record_id = record.record_id or record._data.get('_id', '')
 
102
        record_data = record._data
 
103
        if record_id:
 
104
            self.db[record_id] = record_data
 
105
        else:
 
106
            record_id = self._add_record(record_data)
 
107
        return record_id
 
108
 
 
109
    def _add_record(self, data):
 
110
        """Add a new record to the storage backend."""
 
111
        return self.db.create(data)
 
112
 
 
113
    #FIXME: NEEDS TEST
 
114
    def delete_record(self, record_id):
 
115
        """Delete record with given id"""
 
116
        record = self.db[record_id]
 
117
        record.setdefault('application_annotations', {}).setdefault(
 
118
            'Ubuntu One', {}).setdefault('private_application_annotations', {})[
 
119
            'deleted'] = True
 
120
        self.db[record_id] = record
 
121
 
 
122
    #FIXME: NEEDS TEST
 
123
    def record_exists(self, record_id):
 
124
        """Check if record with given id exists"""
 
125
        if record_id not in self.db:
 
126
            return False
 
127
        record = self.db[record_id]
 
128
        return 'deleted' not in record.get('application_annotations', {}).get(
 
129
            'Ubuntu One', {}).get('private_application_annotations', {})
 
130
 
 
131