Skip to content

Commit 9cdf350

Browse files
author
Johannes Gijsbers
committed
First cut at exporting/importing file content from and to the hyperdb.
Unfortunately, I currently set the content using "Class.set", which also updates the "activity". So testImportExport is broken for tsearch2 right now. Also did bits of refactoring in hyperdb.FileClass, a tiny step towards making in-database files backend & indexer-independent.
1 parent d9b9771 commit 9cdf350

File tree

4 files changed

+63
-30
lines changed

4 files changed

+63
-30
lines changed

roundup/backends/back_tsearch2.py

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import psycopg
66

77
from roundup import hyperdb
8+
from roundup.support import ensureParentsExist
89
from roundup.backends import back_postgresql, tsearch2_setup, indexer_rdbms
910
from roundup.backends.back_postgresql import db_create, db_nuke, db_command
1011
from roundup.backends.back_postgresql import pg_command, db_exists, Class, IssueClass, FileClass
@@ -198,11 +199,13 @@ def tsearchQuery(self, classname, search_terms):
198199

199200
return [(classname, nodeid) for nodeid in nodeids]
200201

201-
# XXX: we can't reuse hyperdb.FileClass for importing/exporting, so file
202-
# contents will end up in CSV exports for now. Not sure whether this is
203-
# truly a problem. If it is, we should write an importer/exporter that
204-
# converts from the database to the filesystem and vice versa
205-
class FileClass(Class):
202+
class FileClass(hyperdb.FileClass, Class):
203+
'''This class defines a large chunk of data. To support this, it has a
204+
mandatory String property "content" which is typically saved off
205+
externally to the hyperdb.
206+
207+
However, this implementation just stores it in the hyperdb.
208+
'''
206209
def __init__(self, db, classname, **properties):
207210
'''The newly-created class automatically includes the "content" property.,
208211
'''
@@ -215,3 +218,22 @@ def create(self, **propvalues):
215218
if 'type' in self.getprops() and not propvalues.get('type'):
216219
propvalues['type'] = self.default_mime_type
217220
return Class.create(self, **propvalues)
221+
222+
def export_files(self, dirname, nodeid):
223+
dest = self.exportFilename(dirname, nodeid)
224+
ensureParentsExist(dest)
225+
fp = open(dest, "w")
226+
fp.write(self.get(nodeid, "content", default=''))
227+
fp.close()
228+
229+
# XXX: this unfortunately sets the activity on the node to "now", causing
230+
# testImportExport to fail. We either need to create a way to disable
231+
# setting activity/actor or we need to override import_list so it already
232+
# includes the "content" property. However, that would mean changing its
233+
# call signature, as we need to know `dirname`.
234+
def import_files(self, dirname, nodeid):
235+
source = self.exportFilename(dirname, nodeid)
236+
237+
fp = open(source, "r")
238+
self.set(nodeid, content=fp.read())
239+
fp.close()

roundup/backends/blobfiles.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: blobfiles.py,v 1.17 2004-11-25 23:53:31 richard Exp $
18+
#$Id: blobfiles.py,v 1.18 2004-12-18 11:12:04 jlgijsbers Exp $
1919
'''This module exports file storage for roundup backends.
2020
Files are stored into a directory hierarchy.
2121
'''
@@ -36,25 +36,30 @@ def files_in_dir(dir):
3636
return num_files
3737

3838
class FileStorage:
39-
"""Store files in some directory structure"""
40-
def filename(self, classname, nodeid, property=None, create=0):
41-
'''Determine what the filename for the given node and optionally
42-
property is.
43-
44-
Try a variety of different filenames - the file could be in the
45-
usual place, or it could be in a temp file pre-commit *or* it
46-
could be in an old-style, backwards-compatible flat directory.
47-
'''
39+
"""Store files in some directory structure"""
40+
def subdirFilename(self, classname, nodeid, property=None):
41+
"""Determine what the filename and subdir for nodeid + classname is."""
4842
if property:
4943
name = '%s%s.%s'%(classname, nodeid, property)
5044
else:
5145
# roundupdb.FileClass never specified the property name, so don't
5246
# include it
5347
name = '%s%s'%(classname, nodeid)
54-
48+
5549
# have a separate subdir for every thousand messages
5650
subdir = str(int(nodeid) / 1000)
57-
filename = os.path.join(self.dir, 'files', classname, subdir, name)
51+
return os.path.join(subdir, name)
52+
53+
def filename(self, classname, nodeid, property=None, create=0):
54+
'''Determine what the filename for the given node and optionally
55+
property is.
56+
57+
Try a variety of different filenames - the file could be in the
58+
usual place, or it could be in a temp file pre-commit *or* it
59+
could be in an old-style, backwards-compatible flat directory.
60+
'''
61+
filename = os.path.join(self.dir, 'files', classname,
62+
self.subdirFilename(classname, nodeid, property))
5863
if create or os.path.exists(filename):
5964
return filename
6065

roundup/hyperdb.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: hyperdb.py,v 1.107 2004-11-25 22:51:06 richard Exp $
18+
# $Id: hyperdb.py,v 1.108 2004-12-18 11:12:03 jlgijsbers Exp $
1919

2020
"""Hyperdatabase implementation, especially field types.
2121
"""
@@ -26,6 +26,7 @@
2626

2727
# roundup modules
2828
import date, password
29+
from support import ensureParentsExist
2930

3031
#
3132
# Types
@@ -746,27 +747,26 @@ def export_propnames(self):
746747
propnames.sort()
747748
return propnames
748749

750+
def exportFilename(self, dirname, nodeid):
751+
subdir_filename = self.db.subdirFilename(self.classname, nodeid)
752+
return os.path.join(dirname, self.classname+'-files', subdir_filename)
753+
749754
def export_files(self, dirname, nodeid):
750755
''' Export the "content" property as a file, not csv column
751756
'''
752757
source = self.db.filename(self.classname, nodeid)
753-
x, filename = os.path.split(source)
754-
x, subdir = os.path.split(x)
755-
dest = os.path.join(dirname, self.classname+'-files', subdir, filename)
756-
if not os.path.exists(os.path.dirname(dest)):
757-
os.makedirs(os.path.dirname(dest))
758-
shutil.copyfile(source, dest)
758+
759+
dest = self.exportFilename(dirname, nodeid)
760+
ensureParentsExist(dest)
761+
shutil.copyfile(source, dest)
759762

760763
def import_files(self, dirname, nodeid):
761764
''' Import the "content" property as a file
762765
'''
766+
source = self.exportFilename(dirname, nodeid)
767+
763768
dest = self.db.filename(self.classname, nodeid, create=1)
764-
x, filename = os.path.split(dest)
765-
x, subdir = os.path.split(x)
766-
source = os.path.join(dirname, self.classname+'-files', subdir,
767-
filename)
768-
if not os.path.exists(os.path.dirname(dest)):
769-
os.makedirs(os.path.dirname(dest))
769+
ensureParentsExist(dest)
770770
shutil.copyfile(source, dest)
771771

772772
class Node:

roundup/support.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
__docformat__ = 'restructuredtext'
66

7+
import os
8+
79
class TruthDict:
810
'''Returns True for valid keys, False for others.
911
'''
@@ -18,4 +20,8 @@ def __init__(self, keys):
1820
def __getitem__(self, name):
1921
return self.keys.has_key(name)
2022

23+
def ensureParentsExist(dest):
24+
if not os.path.exists(os.path.dirname(dest)):
25+
os.makedirs(os.path.dirname(dest))
26+
2127
# vim: set et sts=4 sw=4 :

0 commit comments

Comments
 (0)