Skip to content

Commit 529d12a

Browse files
author
Richard Jones
committed
merge from HEAD
1 parent 92a4b19 commit 529d12a

File tree

8 files changed

+126
-85
lines changed

8 files changed

+126
-85
lines changed

CHANGES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Fixed:
1515
- fixed REMOTE_USER (external HTTP Basic auth) (sf bug 977309)
1616
- fixed roundup-admin "find" to use better value parsing
1717
- fixed RDBMS Class.find() to handle None value in multiple find
18+
- export now stores file "content" in separate files in export directory
1819

1920

2021
2004-06-10 0.7.4

roundup/admin.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1717
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1818
#
19-
# $Id: admin.py,v 1.68.2.2 2004-06-23 22:59:17 richard Exp $
19+
# $Id: admin.py,v 1.68.2.3 2004-06-24 07:14:22 richard Exp $
2020

2121
'''Administration commands for maintaining Roundup trackers.
2222
'''
@@ -1031,15 +1031,16 @@ def do_export(self, args):
10311031
writer = rcsv.writer(f, rcsv.colon_separated)
10321032

10331033
properties = cl.getprops()
1034-
propnames = properties.keys()
1035-
propnames.sort()
1034+
propnames = cl.export_propnames()
10361035
fields = propnames[:]
10371036
fields.append('is retired')
10381037
writer.writerow(fields)
10391038

10401039
# all nodes for this class
10411040
for nodeid in cl.getnodeids():
10421041
writer.writerow(cl.export_list(propnames, nodeid))
1042+
if hasattr(cl, 'export_files'):
1043+
cl.export_files(dir, nodeid)
10431044

10441045
# close this file
10451046
f.close()
@@ -1077,7 +1078,11 @@ class to import.
10771078
raise UsageError, _(rcsv.error)
10781079
from roundup import hyperdb
10791080

1080-
for file in os.listdir(args[0]):
1081+
# directory to import from
1082+
dir = args[0]
1083+
1084+
# import all the files
1085+
for file in os.listdir(dir):
10811086
classname, ext = os.path.splitext(file)
10821087
# we only care about CSV files
10831088
if ext != '.csv' or classname.endswith('-journals'):
@@ -1086,7 +1091,7 @@ class to import.
10861091
cl = self.get_class(classname)
10871092

10881093
# ensure that the properties and the CSV file headings match
1089-
f = open(os.path.join(args[0], file))
1094+
f = open(os.path.join(dir, file))
10901095
reader = rcsv.reader(f, rcsv.colon_separated)
10911096
file_props = None
10921097
maxid = 1
@@ -1096,7 +1101,11 @@ class to import.
10961101
file_props = r
10971102
continue
10981103
# do the import and figure the current highest nodeid
1099-
maxid = max(maxid, int(cl.import_list(file_props, r)))
1104+
nodeid = int(cl.import_list(file_props, r))
1105+
if hasattr(cl, 'import_files'):
1106+
cl.import_files(dir, nodeid)
1107+
maxid = max(maxid, nodeid)
1108+
11001109
f.close()
11011110

11021111
# import the journals

roundup/backends/back_anydbm.py

Lines changed: 1 addition & 21 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: back_anydbm.py,v 1.146.2.10 2004-06-21 05:42:45 richard Exp $
18+
#$Id: back_anydbm.py,v 1.146.2.11 2004-06-24 07:14:26 richard Exp $
1919
'''This module defines a backend that saves the hyperdatabase in a
2020
database chosen by anydbm. It is guaranteed to always be available in python
2121
versions >2.1.1 (the dumbdbm fallback in 2.1.1 and earlier has several
@@ -2118,25 +2118,6 @@ def create(self, **propvalues):
21182118
self.db.storefile(self.classname, newid, None, content)
21192119
return newid
21202120

2121-
def import_list(self, propnames, proplist):
2122-
''' Trap the "content" property...
2123-
'''
2124-
# dupe this list so we don't affect others
2125-
propnames = propnames[:]
2126-
2127-
# extract the "content" property from the proplist
2128-
i = propnames.index('content')
2129-
content = eval(proplist[i])
2130-
del propnames[i]
2131-
del proplist[i]
2132-
2133-
# do the normal import
2134-
newid = Class.import_list(self, propnames, proplist)
2135-
2136-
# save off the "content" file
2137-
self.db.storefile(self.classname, newid, None, content)
2138-
return newid
2139-
21402121
def get(self, nodeid, propname, default=_marker, cache=1):
21412122
''' Trap the content propname and get it from the file
21422123
@@ -2194,7 +2175,6 @@ def getprops(self, protected=1):
21942175
d['content'] = hyperdb.String()
21952176
return d
21962177

2197-
21982178
# deviation from spec - was called ItemClass
21992179
class IssueClass(Class, roundupdb.IssueClass):
22002180
# Overridden methods:

roundup/backends/back_metakit.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: back_metakit.py,v 1.70.2.3 2004-06-08 05:34:21 richard Exp $
1+
# $Id: back_metakit.py,v 1.70.2.4 2004-06-24 07:14:47 richard Exp $
22
'''Metakit backend for Roundup, originally by Gordon McMillan.
33
44
Known Current Bugs:
@@ -44,7 +44,7 @@
4444
from roundup import hyperdb, date, password, roundupdb, security
4545
import metakit
4646
from sessions_dbm import Sessions, OneTimeKeys
47-
import re, marshal, os, sys, time, calendar
47+
import re, marshal, os, sys, time, calendar, shutil
4848
from indexer_dbm import Indexer
4949
import locking
5050
from roundup.date import Range
@@ -496,7 +496,7 @@ def create_inner(self, **propvalues):
496496
ndx = self.getview(READWRITE).append(rowdict)
497497
propvalues['#ISNEW'] = 1
498498
try:
499-
self.set(str(newid), **propvalues)
499+
self.set_inner(str(newid), **propvalues)
500500
except Exception:
501501
self.maxid -= 1
502502
raise
@@ -801,7 +801,7 @@ def set_inner(self, nodeid, **propvalues):
801801
oldnode[key] = oldvalue
802802

803803
# nothing to do?
804-
if not propvalues:
804+
if not isnew and not propvalues:
805805
return propvalues, oldnode
806806
if not propvalues.has_key('activity'):
807807
row.activity = int(time.time())
@@ -1832,6 +1832,29 @@ def gen_filename(self, nodeid):
18321832
os.makedirs(d)
18331833
return os.path.join(d, nm)
18341834

1835+
def export_files(self, dirname, nodeid):
1836+
''' Export the "content" property as a file, not csv column
1837+
'''
1838+
source = self.gen_filename(nodeid)
1839+
x, filename = os.path.split(source)
1840+
x, subdir = os.path.split(x)
1841+
dest = os.path.join(dirname, self.classname+'-files', subdir, filename)
1842+
if not os.path.exists(os.path.dirname(dest)):
1843+
os.makedirs(os.path.dirname(dest))
1844+
shutil.copyfile(source, dest)
1845+
1846+
def import_files(self, dirname, nodeid):
1847+
''' Import the "content" property as a file
1848+
'''
1849+
dest = self.gen_filename(nodeid)
1850+
x, filename = os.path.split(dest)
1851+
x, subdir = os.path.split(x)
1852+
source = os.path.join(dirname, self.classname+'-files', subdir,
1853+
filename)
1854+
if not os.path.exists(os.path.dirname(dest)):
1855+
os.makedirs(os.path.dirname(dest))
1856+
shutil.copyfile(source, dest)
1857+
18351858
def get(self, nodeid, propname, default=_marker, cache=1):
18361859
if propname == 'content':
18371860
poss_msg = 'Possibly an access right configuration problem.'

roundup/backends/blobfiles.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
#$Id: blobfiles.py,v 1.12 2004-03-19 04:47:59 richard Exp $
18+
#$Id: blobfiles.py,v 1.12.2.1 2004-06-24 07:14:48 richard Exp $
1919
'''This module exports file storage for roundup backends.
2020
Files are stored into a directory hierarchy.
2121
'''
2222
__docformat__ = 'restructuredtext'
2323

2424
import os
2525

26-
def files_in_dir(dir):
26+
def files_in_dir(dir):
2727
if not os.path.exists(dir):
2828
return 0
2929
num_files = 0

roundup/backends/rdbms_common.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# $Id: rdbms_common.py,v 1.98.2.10 2004-06-23 23:21:32 richard Exp $
1+
# $Id: rdbms_common.py,v 1.98.2.11 2004-06-24 07:14:48 richard Exp $
22
''' Relational database (SQL) backend common code.
33
44
Basics:
@@ -2575,25 +2575,6 @@ def create(self, **propvalues):
25752575
self.db.storefile(self.classname, newid, None, content)
25762576
return newid
25772577

2578-
def import_list(self, propnames, proplist):
2579-
''' Trap the "content" property...
2580-
'''
2581-
# dupe this list so we don't affect others
2582-
propnames = propnames[:]
2583-
2584-
# extract the "content" property from the proplist
2585-
i = propnames.index('content')
2586-
content = eval(proplist[i])
2587-
del propnames[i]
2588-
del proplist[i]
2589-
2590-
# do the normal import
2591-
newid = Class.import_list(self, propnames, proplist)
2592-
2593-
# save off the "content" file
2594-
self.db.storefile(self.classname, newid, None, content)
2595-
return newid
2596-
25972578
_marker = []
25982579
def get(self, nodeid, propname, default=_marker, cache=1):
25992580
''' Trap the content propname and get it from the file

roundup/hyperdb.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@
1515
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
1616
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
1717
#
18-
# $Id: hyperdb.py,v 1.97.2.1 2004-05-18 21:50:30 richard Exp $
18+
# $Id: hyperdb.py,v 1.97.2.2 2004-06-24 07:14:23 richard Exp $
1919

2020
"""Hyperdatabase implementation, especially field types.
2121
"""
2222
__docformat__ = 'restructuredtext'
2323

2424
# standard python modules
25-
import sys, os, time, re
25+
import sys, os, time, re, shutil
2626

2727
# roundup modules
2828
import date, password
@@ -588,6 +588,13 @@ def safeget(self, nodeid, propname, default=None):
588588
except IndexError:
589589
return default
590590

591+
def export_propnames(self):
592+
'''List the property names for export from this Class.'''
593+
propnames = self.getprops().keys()
594+
propnames.sort()
595+
return propnames
596+
597+
591598
class HyperdbValueError(ValueError):
592599
''' Error converting a raw value into a Hyperdb value '''
593600
pass
@@ -758,7 +765,36 @@ class FileClass:
758765
''' A class that requires the "content" property and stores it on
759766
disk.
760767
'''
761-
pass
768+
def export_propnames(self):
769+
''' Don't export the "content" property
770+
'''
771+
propnames = self.getprops().keys()
772+
propnames.remove('content')
773+
propnames.sort()
774+
return propnames
775+
776+
def export_files(self, dirname, nodeid):
777+
''' Export the "content" property as a file, not csv column
778+
'''
779+
source = self.db.filename(self.classname, nodeid)
780+
x, filename = os.path.split(source)
781+
x, subdir = os.path.split(x)
782+
dest = os.path.join(dirname, self.classname+'-files', subdir, filename)
783+
if not os.path.exists(os.path.dirname(dest)):
784+
os.makedirs(os.path.dirname(dest))
785+
shutil.copyfile(source, dest)
786+
787+
def import_files(self, dirname, nodeid):
788+
''' Import the "content" property as a file
789+
'''
790+
dest = self.db.filename(self.classname, nodeid)
791+
x, filename = os.path.split(dest)
792+
x, subdir = os.path.split(x)
793+
source = os.path.join(dirname, self.classname+'-files', subdir,
794+
filename)
795+
if not os.path.exists(os.path.dirname(dest)):
796+
os.makedirs(os.path.dirname(dest))
797+
shutil.copyfile(source, dest)
762798

763799
class Node:
764800
''' A convenience wrapper for the given node

test/db_test_base.py

Lines changed: 40 additions & 29 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: db_test_base.py,v 1.27.2.6 2004-06-23 23:21:32 richard Exp $
18+
# $Id: db_test_base.py,v 1.27.2.7 2004-06-24 07:14:49 richard Exp $
1919

2020
import unittest, os, shutil, errno, imp, sys, time, pprint
2121

@@ -846,6 +846,8 @@ def filteringSetup(self):
846846
'foo': date.Interval('0:10'), 'priority': '1',
847847
'nosy': ['1'], 'deadline': date.Date('2004-03-08')}):
848848
self.db.issue.create(**issue)
849+
file_content = ''.join([chr(i) for i in range(255)])
850+
self.db.file.create(content=file_content)
849851
self.db.commit()
850852
return self.assertEqual, self.db.issue.filter
851853

@@ -981,34 +983,43 @@ def testImportExport(self):
981983
for name in klass.getprops().keys():
982984
it[name] = klass.get(id, name)
983985

984-
# grab the export
985-
export = {}
986-
journals = {}
987-
for cn,klass in self.db.classes.items():
988-
names = klass.getprops().keys()
989-
cl = export[cn] = [names+['is retired']]
990-
for id in klass.getnodeids():
991-
cl.append(klass.export_list(names, id))
992-
journals[cn] = klass.export_journals()
993-
994-
# shut down this db and nuke it
995-
self.db.close()
996-
self.nuke_database()
997-
998-
# open a new, empty database
999-
os.makedirs(config.DATABASE + '/files')
1000-
self.db = self.module.Database(config, 'admin')
1001-
setupSchema(self.db, 0, self.module)
1002-
1003-
# import
1004-
for cn, items in export.items():
1005-
klass = self.db.classes[cn]
1006-
names = items[0]
1007-
maxid = 1
1008-
for itemprops in items[1:]:
1009-
maxid = max(maxid, int(klass.import_list(names, itemprops)))
1010-
self.db.setid(cn, str(maxid+1))
1011-
klass.import_journals(journals[cn])
986+
os.mkdir('_test_export')
987+
try:
988+
# grab the export
989+
export = {}
990+
journals = {}
991+
for cn,klass in self.db.classes.items():
992+
names = klass.export_propnames()
993+
cl = export[cn] = [names+['is retired']]
994+
for id in klass.getnodeids():
995+
cl.append(klass.export_list(names, id))
996+
if hasattr(klass, 'export_files'):
997+
klass.export_files('_test_export', id)
998+
journals[cn] = klass.export_journals()
999+
1000+
# shut down this db and nuke it
1001+
self.db.close()
1002+
self.nuke_database()
1003+
1004+
# open a new, empty database
1005+
os.makedirs(config.DATABASE + '/files')
1006+
self.db = self.module.Database(config, 'admin')
1007+
setupSchema(self.db, 0, self.module)
1008+
1009+
# import
1010+
for cn, items in export.items():
1011+
klass = self.db.classes[cn]
1012+
names = items[0]
1013+
maxid = 1
1014+
for itemprops in items[1:]:
1015+
id = int(klass.import_list(names, itemprops))
1016+
if hasattr(klass, 'import_files'):
1017+
klass.import_files('_test_export', id)
1018+
maxid = max(maxid, id)
1019+
self.db.setid(cn, str(maxid+1))
1020+
klass.import_journals(journals[cn])
1021+
finally:
1022+
shutil.rmtree('_test_export')
10121023

10131024
# compare with snapshot of the database
10141025
for cn, items in orig.items():

0 commit comments

Comments
 (0)