comparison roundup/admin.py @ 952:f615fbd02c18

full database export and import is done
author Richard Jones <richard@users.sourceforge.net>
date Mon, 19 Aug 2002 02:53:27 +0000
parents c67297b55a34
children 04a6b3bfbf23
comparison
equal deleted inserted replaced
951:3b3808ad86dc 952:f615fbd02c18
14 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 14 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
15 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" 15 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
16 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, 16 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
17 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 17 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
18 # 18 #
19 # $Id: admin.py,v 1.22 2002-08-16 04:26:42 richard Exp $ 19 # $Id: admin.py,v 1.23 2002-08-19 02:53:27 richard Exp $
20 20
21 import sys, os, getpass, getopt, re, UserDict, shlex, shutil 21 import sys, os, getpass, getopt, re, UserDict, shlex, shutil
22 try: 22 try:
23 import csv 23 import csv
24 except ImportError: 24 except ImportError:
806 except IndexError: 806 except IndexError:
807 raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals() 807 raise UsageError, _('no such %(classname)s node "%(nodeid)s"')%locals()
808 return 0 808 return 0
809 809
810 def do_export(self, args): 810 def do_export(self, args):
811 '''Usage: export [class[,class]] destination_dir 811 '''Usage: export [class[,class]] export_dir
812 Export the database to tab-separated-value files. 812 Export the database to tab-separated-value files.
813 813
814 This action exports the current data from the database into 814 This action exports the current data from the database into
815 tab-separated-value files that are placed in the nominated destination 815 tab-separated-value files that are placed in the nominated destination
816 directory. The journals are not exported. 816 directory. The journals are not exported.
817 ''' 817 '''
818 # we need the CSV module
819 if csv is None:
820 raise UsageError, \
821 _('Sorry, you need the csv module to use this function.\n'
822 'Get it from: http://www.object-craft.com.au/projects/csv/')
823
818 # grab the directory to export to 824 # grab the directory to export to
819 if len(args) < 1: 825 if len(args) < 1:
820 raise UsageError, _('Not enough arguments supplied') 826 raise UsageError, _('Not enough arguments supplied')
821 dir = args[-1] 827 dir = args[-1]
822 828
825 classes = args[0].split(',') 831 classes = args[0].split(',')
826 else: 832 else:
827 classes = self.db.classes.keys() 833 classes = self.db.classes.keys()
828 834
829 # use the csv parser if we can - it's faster 835 # use the csv parser if we can - it's faster
830 if csv is not None: 836 p = csv.parser(field_sep=':')
831 p = csv.parser(field_sep=':')
832 837
833 # do all the classes specified 838 # do all the classes specified
834 for classname in classes: 839 for classname in classes:
835 cl = self.get_class(classname) 840 cl = self.get_class(classname)
836 f = open(os.path.join(dir, classname+'.csv'), 'w') 841 f = open(os.path.join(dir, classname+'.csv'), 'w')
837 f.write(':'.join(cl.properties.keys()) + '\n') 842 properties = cl.getprops()
843 propnames = properties.keys()
844 propnames.sort()
845 print >> f, p.join(propnames)
838 846
839 # all nodes for this class 847 # all nodes for this class
840 properties = cl.getprops()
841 for nodeid in cl.list(): 848 for nodeid in cl.list():
842 l = [] 849 print >>f, p.join(cl.export_list(propnames, nodeid))
843 for prop, proptype in properties:
844 value = cl.get(nodeid, prop)
845 # convert data where needed
846 if isinstance(proptype, hyperdb.Date):
847 value = value.get_tuple()
848 elif isinstance(proptype, hyperdb.Interval):
849 value = value.get_tuple()
850 elif isinstance(proptype, hyperdb.Password):
851 value = str(value)
852 l.append(repr(value))
853
854 # now write
855 if csv is not None:
856 f.write(p.join(l) + '\n')
857 else:
858 # escape the individual entries to they're valid CSV
859 m = []
860 for entry in l:
861 if '"' in entry:
862 entry = '""'.join(entry.split('"'))
863 if ':' in entry:
864 entry = '"%s"'%entry
865 m.append(entry)
866 f.write(':'.join(m) + '\n')
867 return 0 850 return 0
868 851
869 def do_import(self, args): 852 def do_import(self, args):
870 '''Usage: import class file 853 '''Usage: import import_dir
871 Import the contents of the tab-separated-value file. 854 Import a database from the directory containing CSV files, one per
872 855 class to import.
873 The file must define the same properties as the class (including having 856
874 a "header" line with those property names.) The new nodes are added to 857 The files must define the same properties as the class (including having
875 the existing database - if you want to create a new database using the 858 a "header" line with those property names.)
876 imported data, then create a new database (or, tediously, retire all 859
877 the old data.) 860 The imported nodes will have the same nodeid as defined in the
878 ''' 861 import file, thus replacing any existing content.
879 if len(args) < 2: 862
863 XXX The new nodes are added to the existing database - if you want to
864 XXX create a new database using the imported data, then create a new
865 XXX database (or, tediously, retire all the old data.)
866 '''
867 if len(args) < 1:
880 raise UsageError, _('Not enough arguments supplied') 868 raise UsageError, _('Not enough arguments supplied')
881 if csv is None: 869 if csv is None:
882 raise UsageError, \ 870 raise UsageError, \
883 _('Sorry, you need the csv module to use this function.\n' 871 _('Sorry, you need the csv module to use this function.\n'
884 'Get it from: http://www.object-craft.com.au/projects/csv/') 872 'Get it from: http://www.object-craft.com.au/projects/csv/')
885 873
886 from roundup import hyperdb 874 from roundup import hyperdb
887 875
888 # ensure that the properties and the CSV file headings match 876 for file in os.listdir(args[0]):
889 classname = args[0] 877 f = open(os.path.join(args[0], file))
890 cl = self.get_class(classname) 878
891 f = open(args[1]) 879 # get the classname
892 p = csv.parser(field_sep=':') 880 classname = os.path.splitext(file)[0]
893 file_props = p.parse(f.readline()) 881
894 props = cl.properties.keys() 882 # ensure that the properties and the CSV file headings match
895 m = file_props[:] 883 cl = self.get_class(classname)
896 m.sort() 884 p = csv.parser(field_sep=':')
897 props.sort() 885 file_props = p.parse(f.readline())
898 if m != props: 886 properties = cl.getprops()
899 raise UsageError, _('Import file doesn\'t define the same ' 887 propnames = properties.keys()
900 'properties as "%(arg0)s".')%{'arg0': args[0]} 888 propnames.sort()
901 889 m = file_props[:]
902 # loop through the file and create a node for each entry 890 m.sort()
903 n = range(len(props)) 891 if m != propnames:
904 while 1: 892 raise UsageError, _('Import file doesn\'t define the same '
905 line = f.readline() 893 'properties as "%(arg0)s".')%{'arg0': args[0]}
906 if not line: break 894
907 895 # loop through the file and create a node for each entry
908 # parse lines until we get a complete entry 896 maxid = 1
909 while 1: 897 while 1:
910 l = p.parse(line)
911 if l: break
912 line = f.readline() 898 line = f.readline()
913 if not line: 899 if not line: break
914 raise ValueError, "Unexpected EOF during CSV parse" 900
915 901 # parse lines until we get a complete entry
916 # make the new node's property map 902 while 1:
917 d = {} 903 l = p.parse(line)
918 for i in n: 904 if l: break
919 # Use eval to reverse the repr() used to output the CSV 905 line = f.readline()
920 value = eval(l[i]) 906 if not line:
921 # Figure the property for this column 907 raise ValueError, "Unexpected EOF during CSV parse"
922 key = file_props[i] 908
923 proptype = cl.properties[key] 909 # do the import and figure the current highest nodeid
924 # Convert for property type 910 maxid = max(maxid, int(cl.import_list(propnames, l)))
925 if isinstance(proptype, hyperdb.Date): 911
926 value = date.Date(value) 912 print 'setting', classname, maxid
927 elif isinstance(proptype, hyperdb.Interval): 913 self.db.setid(classname, str(maxid))
928 value = date.Interval(value)
929 elif isinstance(proptype, hyperdb.Password):
930 pwd = password.Password()
931 pwd.unpack(value)
932 value = pwd
933 if value is not None:
934 d[key] = value
935
936 # and create the new node
937 apply(cl.create, (), d)
938 return 0 914 return 0
939 915
940 def do_pack(self, args): 916 def do_pack(self, args):
941 '''Usage: pack period | date 917 '''Usage: pack period | date
942 918
1168 tool = AdminTool() 1144 tool = AdminTool()
1169 sys.exit(tool.main()) 1145 sys.exit(tool.main())
1170 1146
1171 # 1147 #
1172 # $Log: not supported by cvs2svn $ 1148 # $Log: not supported by cvs2svn $
1149 # Revision 1.22 2002/08/16 04:26:42 richard
1150 # moving towards full database export
1151 #
1173 # Revision 1.21 2002/08/01 01:07:37 richard 1152 # Revision 1.21 2002/08/01 01:07:37 richard
1174 # include info about new user roles 1153 # include info about new user roles
1175 # 1154 #
1176 # Revision 1.20 2002/08/01 00:56:22 richard 1155 # Revision 1.20 2002/08/01 00:56:22 richard
1177 # Added the web access and email access permissions, so people can restrict 1156 # Added the web access and email access permissions, so people can restrict

Roundup Issue Tracker: http://roundup-tracker.org/