Mercurial > p > roundup > code
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 |
