1.1 --- a/imiptools/stores/common.py Thu Apr 21 23:17:35 2016 +0200
1.2 +++ b/imiptools/stores/common.py Thu Apr 21 23:20:57 2016 +0200
1.3 @@ -35,26 +35,57 @@
1.4
1.5 # Event and event metadata access.
1.6
1.7 + def get_all_events(self, user, dirname=None):
1.8 +
1.9 + """
1.10 + Return a set of (uid, recurrenceid) tuples for all events. Unless
1.11 + 'dirname' is specified, only active events are returned; otherwise,
1.12 + events from the given 'dirname' are returned.
1.13 + """
1.14 +
1.15 + cancelled = self.get_cancelled_events(user)
1.16 + active = self.get_events(user)
1.17 +
1.18 + if dirname == "cancellations":
1.19 + uids = cancelled
1.20 + else:
1.21 + uids = active
1.22 +
1.23 + if not uids:
1.24 + return set()
1.25 +
1.26 + all_events = set()
1.27 +
1.28 + # Add all qualifying parent events to the result set.
1.29 +
1.30 + for uid in uids:
1.31 + all_events.add((uid, None))
1.32 +
1.33 + # Process all parent events regardless of status to find those in the
1.34 + # category/directory of interest.
1.35 +
1.36 + for uid in active + cancelled:
1.37 +
1.38 + if dirname == "cancellations":
1.39 + recurrenceids = self.get_cancelled_recurrences(user, uid)
1.40 + else:
1.41 + recurrenceids = self.get_active_recurrences(user, uid)
1.42 +
1.43 + all_events.update([(uid, recurrenceid) for recurrenceid in recurrenceids])
1.44 +
1.45 + return all_events
1.46 +
1.47 def get_events(self, user):
1.48
1.49 "Return a list of event identifiers."
1.50
1.51 pass
1.52
1.53 - def get_all_events(self, user):
1.54 -
1.55 - "Return a set of (uid, recurrenceid) tuples for all events."
1.56 + def get_cancelled_events(self, user):
1.57
1.58 - uids = self.get_events(user)
1.59 - if not uids:
1.60 - return set()
1.61 + "Return a list of event identifiers for cancelled events."
1.62
1.63 - all_events = set()
1.64 - for uid in uids:
1.65 - all_events.add((uid, None))
1.66 - all_events.update([(uid, recurrenceid) for recurrenceid in self.get_recurrences(user, uid)])
1.67 -
1.68 - return all_events
1.69 + pass
1.70
1.71 def get_event(self, user, uid, recurrenceid=None, dirname=None):
1.72
1.73 @@ -301,13 +332,20 @@
1.74
1.75 def get_freebusy_for_update(self, user, name=None):
1.76
1.77 - "Get free/busy details for the given 'user'."
1.78 + """
1.79 + Get free/busy details for the given 'user' that may be updated,
1.80 + potentially affecting the stored information directly.
1.81 + """
1.82
1.83 return self.get_freebusy(user, name, True)
1.84
1.85 def get_freebusy_for_other_for_update(self, user, other):
1.86
1.87 - "For the given 'user', get free/busy details for the 'other' user."
1.88 + """
1.89 + For the given 'user', get free/busy details for the 'other' user
1.90 + that may be updated, potentially affecting the stored information
1.91 + directly.
1.92 + """
1.93
1.94 return self.get_freebusy_for_other(user, other, True)
1.95
1.96 @@ -323,6 +361,15 @@
1.97
1.98 pass
1.99
1.100 + def get_freebusy_others(self, user):
1.101 +
1.102 + """
1.103 + For the given 'user', return a list of other users for whom free/busy
1.104 + information is retained.
1.105 + """
1.106 +
1.107 + pass
1.108 +
1.109 # Tentative free/busy periods related to countering.
1.110
1.111 def get_freebusy_offers(self, user, mutable=False):
1.112 @@ -333,7 +380,10 @@
1.113
1.114 def get_freebusy_offers_for_update(self, user):
1.115
1.116 - "Get free/busy offers for the given 'user'."
1.117 + """
1.118 + Get free/busy offers for the given 'user' that may be updated,
1.119 + potentially affecting the stored information directly.
1.120 + """
1.121
1.122 return self.get_freebusy_offers(user, True)
1.123
1.124 @@ -552,6 +602,14 @@
1.125
1.126 pass
1.127
1.128 + def set_group(self, quota, store_user, user_group):
1.129 +
1.130 + """
1.131 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
1.132 + """
1.133 +
1.134 + pass
1.135 +
1.136 def get_limits(self, quota):
1.137
1.138 """
1.139 @@ -572,6 +630,15 @@
1.140
1.141 # Free/busy period access for users within quota groups.
1.142
1.143 + def get_freebusy_users(self, quota):
1.144 +
1.145 + """
1.146 + Return a list of users whose free/busy details are retained for the
1.147 + given 'quota'.
1.148 + """
1.149 +
1.150 + pass
1.151 +
1.152 def get_freebusy(self, quota, user, mutable=False):
1.153
1.154 "Get free/busy details for the given 'quota' and 'user'."
1.155 @@ -580,7 +647,10 @@
1.156
1.157 def get_freebusy_for_update(self, quota, user):
1.158
1.159 - "Get free/busy details for the given 'quota' and 'user'."
1.160 + """
1.161 + Get free/busy details for the given 'quota' and 'user' that may be
1.162 + updated, potentially affecting the stored information directly.
1.163 + """
1.164
1.165 return self.get_freebusy(quota, user, True)
1.166
1.167 @@ -605,7 +675,8 @@
1.168
1.169 """
1.170 Return a list of journal entries for the given 'quota' for the indicated
1.171 - 'group'.
1.172 + 'group' that may be updated, potentially affecting the stored
1.173 + information directly.
1.174 """
1.175
1.176 return self.get_entries(quota, group, True)
2.1 --- a/imiptools/stores/database/common.py Thu Apr 21 23:17:35 2016 +0200
2.2 +++ b/imiptools/stores/database/common.py Thu Apr 21 23:20:57 2016 +0200
2.3 @@ -58,12 +58,36 @@
2.4
2.5 # Event and event metadata access.
2.6
2.7 - def get_events(self, user):
2.8 + def get_all_events(self, user, dirname=None):
2.9 +
2.10 + """
2.11 + Return a set of (uid, recurrenceid) tuples for all events. Unless
2.12 + 'dirname' is specified, only active events are returned; otherwise,
2.13 + events from the given 'dirname' are returned.
2.14 + """
2.15 +
2.16 + columns, values = self.get_event_table_filters(dirname)
2.17 +
2.18 + columns += ["store_user"]
2.19 + values += [user]
2.20 +
2.21 + query, values = self.get_query(
2.22 + "select object_uid, null as object_recurrenceid from objects :condition "
2.23 + "union all "
2.24 + "select object_uid, object_recurrenceid from recurrences :condition",
2.25 + columns, values)
2.26 +
2.27 + self.cursor.execute(query, values)
2.28 + return self.cursor.fetchall()
2.29 +
2.30 + def get_events(self, user, dirname=None):
2.31
2.32 "Return a list of event identifiers."
2.33
2.34 - columns = ["store_user", "status"]
2.35 - values = [user, "active"]
2.36 + columns, values = self.get_event_table_filters(dirname)
2.37 +
2.38 + columns += ["store_user"]
2.39 + values += [user]
2.40
2.41 query, values = self.get_query(
2.42 "select object_uid from objects :condition",
2.43 @@ -72,36 +96,11 @@
2.44 self.cursor.execute(query, values)
2.45 return [r[0] for r in self.cursor.fetchall()]
2.46
2.47 - def get_all_events(self, user):
2.48 -
2.49 - "Return a set of (uid, recurrenceid) tuples for all events."
2.50 -
2.51 - query, values = self.get_query(
2.52 - "select object_uid, null as object_recurrenceid from objects :condition "
2.53 - "union all "
2.54 - "select object_uid, object_recurrenceid from recurrences :condition",
2.55 - ["store_user"], [user])
2.56 -
2.57 - self.cursor.execute(query, values)
2.58 - return self.cursor.fetchall()
2.59 -
2.60 - def get_event_table(self, recurrenceid=None, dirname=None):
2.61 + def get_cancelled_events(self, user):
2.62
2.63 - "Get the table providing events for any specified 'dirname'."
2.64 -
2.65 - if recurrenceid:
2.66 - return self.get_recurrence_table(dirname)
2.67 - else:
2.68 - return self.get_complete_event_table(dirname)
2.69 + "Return a list of event identifiers for cancelled events."
2.70
2.71 - def get_event_table_filters(self, dirname=None):
2.72 -
2.73 - "Get filter details for any specified 'dirname'."
2.74 -
2.75 - if dirname == "cancellations":
2.76 - return ["status"], ["cancelled"]
2.77 - else:
2.78 - return ["status"], ["active"]
2.79 + return self.get_events(user, "cancellations")
2.80
2.81 def get_event(self, user, uid, recurrenceid=None, dirname=None):
2.82
2.83 @@ -131,15 +130,6 @@
2.84 result = self.cursor.fetchone()
2.85 return result and parse_string(result[0], "utf-8")
2.86
2.87 - def get_complete_event_table(self, dirname=None):
2.88 -
2.89 - "Get the table providing events for any specified 'dirname'."
2.90 -
2.91 - if dirname == "counters":
2.92 - return "countered_objects"
2.93 - else:
2.94 - return "objects"
2.95 -
2.96 def get_complete_event(self, user, uid):
2.97
2.98 "Get the event for the given 'user' with the given 'uid'."
2.99 @@ -231,15 +221,6 @@
2.100 self.cursor.execute(query, values)
2.101 return [t[0] for t in self.cursor.fetchall() or []]
2.102
2.103 - def get_recurrence_table(self, dirname=None):
2.104 -
2.105 - "Get the table providing recurrences for any specified 'dirname'."
2.106 -
2.107 - if dirname == "counters":
2.108 - return "countered_recurrences"
2.109 - else:
2.110 - return "recurrences"
2.111 -
2.112 def get_recurrence(self, user, uid, recurrenceid):
2.113
2.114 """
2.115 @@ -320,6 +301,44 @@
2.116 self.cursor.execute(query, values)
2.117 return True
2.118
2.119 + # Event table computation.
2.120 +
2.121 + def get_event_table(self, recurrenceid=None, dirname=None):
2.122 +
2.123 + "Get the table providing events for any specified 'dirname'."
2.124 +
2.125 + if recurrenceid:
2.126 + return self.get_recurrence_table(dirname)
2.127 + else:
2.128 + return self.get_complete_event_table(dirname)
2.129 +
2.130 + def get_event_table_filters(self, dirname=None):
2.131 +
2.132 + "Get filter details for any specified 'dirname'."
2.133 +
2.134 + if dirname == "cancellations":
2.135 + return ["status"], ["cancelled"]
2.136 + else:
2.137 + return ["status"], ["active"]
2.138 +
2.139 + def get_complete_event_table(self, dirname=None):
2.140 +
2.141 + "Get the table providing events for any specified 'dirname'."
2.142 +
2.143 + if dirname == "counters":
2.144 + return "countered_objects"
2.145 + else:
2.146 + return "objects"
2.147 +
2.148 + def get_recurrence_table(self, dirname=None):
2.149 +
2.150 + "Get the table providing recurrences for any specified 'dirname'."
2.151 +
2.152 + if dirname == "counters":
2.153 + return "countered_recurrences"
2.154 + else:
2.155 + return "recurrences"
2.156 +
2.157 # Free/busy period providers, upon extension of the free/busy records.
2.158
2.159 def _get_freebusy_providers(self, user):
2.160 @@ -444,6 +463,23 @@
2.161
2.162 return True
2.163
2.164 + def get_freebusy_others(self, user):
2.165 +
2.166 + """
2.167 + For the given 'user', return a list of other users for whom free/busy
2.168 + information is retained.
2.169 + """
2.170 +
2.171 + columns = ["store_user"]
2.172 + values = [user]
2.173 +
2.174 + query, values = self.get_query(
2.175 + "select distinct other from freebusy_others :condition",
2.176 + columns, values)
2.177 +
2.178 + self.cursor.execute(query, values)
2.179 + return [r[0] for r in self.cursor.fetchall()]
2.180 +
2.181 # Tentative free/busy periods related to countering.
2.182
2.183 def get_freebusy_offers(self, user, mutable=False):
2.184 @@ -812,6 +848,36 @@
2.185 self.cursor.execute(query, values)
2.186 return dict(self.cursor.fetchall())
2.187
2.188 + def set_group(self, quota, store_user, user_group):
2.189 +
2.190 + """
2.191 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
2.192 + """
2.193 +
2.194 + columns = ["quota", "store_user"]
2.195 + values = [quota, store_user]
2.196 + setcolumns = ["user_group"]
2.197 + setvalues = [user_group]
2.198 +
2.199 + query, values = self.get_query(
2.200 + "update user_groups :set :condition",
2.201 + columns, values, setcolumns, setvalues)
2.202 +
2.203 + self.cursor.execute(query, values)
2.204 +
2.205 + if self.cursor.rowcount > 0:
2.206 + return True
2.207 +
2.208 + columns = ["quota", "store_user", "user_group"]
2.209 + values = [quota, store_user, user_group]
2.210 +
2.211 + query, values = self.get_query(
2.212 + "insert into user_groups (:columns) values (:values)",
2.213 + columns, values)
2.214 +
2.215 + self.cursor.execute(query, values)
2.216 + return True
2.217 +
2.218 def get_limits(self, quota):
2.219
2.220 """
2.221 @@ -862,6 +928,23 @@
2.222
2.223 # Free/busy period access for users within quota groups.
2.224
2.225 + def get_freebusy_users(self, quota):
2.226 +
2.227 + """
2.228 + Return a list of users whose free/busy details are retained for the
2.229 + given 'quota'.
2.230 + """
2.231 +
2.232 + columns = ["quota"]
2.233 + values = [quota]
2.234 +
2.235 + query, values = self.get_query(
2.236 + "select distinct store_user from user_freebusy :condition",
2.237 + columns, values)
2.238 +
2.239 + self.cursor.execute(query)
2.240 + return [r[0] for r in self.cursor.fetchall()]
2.241 +
2.242 def get_freebusy(self, quota, user, mutable=False):
2.243
2.244 "Get free/busy details for the given 'quota' and 'user'."
3.1 --- a/imiptools/stores/file.py Thu Apr 21 23:17:35 2016 +0200
3.2 +++ b/imiptools/stores/file.py Thu Apr 21 23:20:57 2016 +0200
3.3 @@ -232,25 +232,19 @@
3.4
3.5 filename = self.get_object_in_store(user, "objects")
3.6 if not filename or not isdir(filename):
3.7 - return None
3.8 + return []
3.9
3.10 return [name for name in listdir(filename) if isfile(join(filename, name))]
3.11
3.12 - def get_event_filename(self, user, uid, recurrenceid=None, dirname=None, username=None):
3.13 + def get_cancelled_events(self, user):
3.14
3.15 - """
3.16 - Get the filename providing the event for the given 'user' with the given
3.17 - 'uid'. If the optional 'recurrenceid' is specified, a specific instance
3.18 - or occurrence of an event is returned.
3.19 + "Return a list of event identifiers for cancelled events."
3.20
3.21 - Where 'dirname' is specified, the given directory name is used as the
3.22 - base of the location within which any filename will reside.
3.23 - """
3.24 + filename = self.get_object_in_store(user, "cancellations", "objects")
3.25 + if not filename or not isdir(filename):
3.26 + return []
3.27
3.28 - if recurrenceid:
3.29 - return self.get_recurrence_filename(user, uid, recurrenceid, dirname, username)
3.30 - else:
3.31 - return self.get_complete_event_filename(user, uid, dirname, username)
3.32 + return [name for name in listdir(filename) if isfile(join(filename, name))]
3.33
3.34 def get_event(self, user, uid, recurrenceid=None, dirname=None):
3.35
3.36 @@ -266,21 +260,6 @@
3.37
3.38 return filename and self._get_object(user, filename)
3.39
3.40 - def get_complete_event_filename(self, user, uid, dirname=None, username=None):
3.41 -
3.42 - """
3.43 - Get the filename providing the event for the given 'user' with the given
3.44 - 'uid'.
3.45 -
3.46 - Where 'dirname' is specified, the given directory name is used as the
3.47 - base of the location within which any filename will reside.
3.48 -
3.49 - Where 'username' is specified, the event details will reside in a file
3.50 - bearing that name within a directory having 'uid' as its name.
3.51 - """
3.52 -
3.53 - return self.get_object_in_store(user, dirname, "objects", uid, username)
3.54 -
3.55 def get_complete_event(self, user, uid):
3.56
3.57 "Get the event for the given 'user' with the given 'uid'."
3.58 @@ -346,21 +325,6 @@
3.59
3.60 return [name for name in listdir(filename) if isfile(join(filename, name))]
3.61
3.62 - def get_recurrence_filename(self, user, uid, recurrenceid, dirname=None, username=None):
3.63 -
3.64 - """
3.65 - For the event of the given 'user' with the given 'uid', return the
3.66 - filename providing the recurrence with the given 'recurrenceid'.
3.67 -
3.68 - Where 'dirname' is specified, the given directory name is used as the
3.69 - base of the location within which any filename will reside.
3.70 -
3.71 - Where 'username' is specified, the event details will reside in a file
3.72 - bearing that name within a directory having 'uid' as its name.
3.73 - """
3.74 -
3.75 - return self.get_object_in_store(user, dirname, "recurrences", uid, recurrenceid, username)
3.76 -
3.77 def get_recurrence(self, user, uid, recurrenceid):
3.78
3.79 """
3.80 @@ -410,6 +374,54 @@
3.81
3.82 return True
3.83
3.84 + # Event filename computation.
3.85 +
3.86 + def get_event_filename(self, user, uid, recurrenceid=None, dirname=None, username=None):
3.87 +
3.88 + """
3.89 + Get the filename providing the event for the given 'user' with the given
3.90 + 'uid'. If the optional 'recurrenceid' is specified, a specific instance
3.91 + or occurrence of an event is returned.
3.92 +
3.93 + Where 'dirname' is specified, the given directory name is used as the
3.94 + base of the location within which any filename will reside.
3.95 + """
3.96 +
3.97 + if recurrenceid:
3.98 + return self.get_recurrence_filename(user, uid, recurrenceid, dirname, username)
3.99 + else:
3.100 + return self.get_complete_event_filename(user, uid, dirname, username)
3.101 +
3.102 + def get_recurrence_filename(self, user, uid, recurrenceid, dirname=None, username=None):
3.103 +
3.104 + """
3.105 + For the event of the given 'user' with the given 'uid', return the
3.106 + filename providing the recurrence with the given 'recurrenceid'.
3.107 +
3.108 + Where 'dirname' is specified, the given directory name is used as the
3.109 + base of the location within which any filename will reside.
3.110 +
3.111 + Where 'username' is specified, the event details will reside in a file
3.112 + bearing that name within a directory having 'uid' as its name.
3.113 + """
3.114 +
3.115 + return self.get_object_in_store(user, dirname, "recurrences", uid, recurrenceid, username)
3.116 +
3.117 + def get_complete_event_filename(self, user, uid, dirname=None, username=None):
3.118 +
3.119 + """
3.120 + Get the filename providing the event for the given 'user' with the given
3.121 + 'uid'.
3.122 +
3.123 + Where 'dirname' is specified, the given directory name is used as the
3.124 + base of the location within which any filename will reside.
3.125 +
3.126 + Where 'username' is specified, the event details will reside in a file
3.127 + bearing that name within a directory having 'uid' as its name.
3.128 + """
3.129 +
3.130 + return self.get_object_in_store(user, dirname, "objects", uid, username)
3.131 +
3.132 # Free/busy period providers, upon extension of the free/busy records.
3.133
3.134 def _get_freebusy_providers(self, user):
3.135 @@ -502,6 +514,20 @@
3.136 map(lambda fb: fb.as_tuple(strings_only=True), freebusy.periods))
3.137 return True
3.138
3.139 + def get_freebusy_others(self, user):
3.140 +
3.141 + """
3.142 + For the given 'user', return a list of other users for whom free/busy
3.143 + information is retained.
3.144 + """
3.145 +
3.146 + filename = self.get_object_in_store(user, "freebusy-other")
3.147 +
3.148 + if not filename or not isdir(filename):
3.149 + return []
3.150 +
3.151 + return listdir(filename)
3.152 +
3.153 # Tentative free/busy periods related to countering.
3.154
3.155 def get_freebusy_offers(self, user, mutable=False):
3.156 @@ -538,7 +564,7 @@
3.157
3.158 filename = self.get_object_in_store(user, queue)
3.159 if not filename or not isfile(filename):
3.160 - return None
3.161 + return []
3.162
3.163 return self._get_table_atomic(user, filename, [(1, None), (2, None)])
3.164
3.165 @@ -609,7 +635,7 @@
3.166
3.167 filename = self.get_event_filename(user, uid, recurrenceid, "counters")
3.168 if not filename or not isdir(filename):
3.169 - return False
3.170 + return []
3.171
3.172 return [name for name in listdir(filename) if isfile(join(filename, name))]
3.173
3.174 @@ -622,7 +648,7 @@
3.175
3.176 filename = self.get_event_filename(user, uid, recurrenceid, "counters", other)
3.177 if not filename or not isfile(filename):
3.178 - return False
3.179 + return None
3.180
3.181 return self._get_object(user, filename)
3.182
3.183 @@ -797,6 +823,22 @@
3.184
3.185 return dict(self._get_table_atomic(quota, filename, tab_separated=False))
3.186
3.187 + def set_group(self, quota, store_user, user_group):
3.188 +
3.189 + """
3.190 + For the given 'quota', set a mapping from 'store_user' to 'user_group'.
3.191 + """
3.192 +
3.193 + filename = self.get_object_in_store(quota, "groups")
3.194 + if not filename:
3.195 + return False
3.196 +
3.197 + groups = self.get_groups(quota) or {}
3.198 + groups[store_user] = user_group
3.199 +
3.200 + self._set_table_atomic(quota, filename, groups.items())
3.201 + return True
3.202 +
3.203 def get_limits(self, quota):
3.204
3.205 """
3.206 @@ -806,7 +848,7 @@
3.207
3.208 filename = self.get_object_in_store(quota, "limits")
3.209 if not filename or not isfile(filename):
3.210 - return None
3.211 + return {}
3.212
3.213 return dict(self._get_table_atomic(quota, filename, tab_separated=False))
3.214
3.215 @@ -819,7 +861,7 @@
3.216
3.217 filename = self.get_object_in_store(quota, "limits")
3.218 if not filename:
3.219 - return None
3.220 + return False
3.221
3.222 limits = self.get_limits(quota) or {}
3.223 limits[group] = limit
3.224 @@ -829,6 +871,19 @@
3.225
3.226 # Free/busy period access for users within quota groups.
3.227
3.228 + def get_freebusy_users(self, quota):
3.229 +
3.230 + """
3.231 + Return a list of users whose free/busy details are retained for the
3.232 + given 'quota'.
3.233 + """
3.234 +
3.235 + filename = self.get_object_in_store(quota, "freebusy")
3.236 + if not filename or not isdir(filename):
3.237 + return []
3.238 +
3.239 + return listdir(filename)
3.240 +
3.241 def get_freebusy(self, quota, user, mutable=False):
3.242
3.243 "Get free/busy details for the given 'quota' and 'user'."
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/tools/copy_store.py Thu Apr 21 23:20:57 2016 +0200
4.3 @@ -0,0 +1,169 @@
4.4 +#!/usr/bin/env python
4.5 +
4.6 +"""
4.7 +Copy store information into another store.
4.8 +
4.9 +Copyright (C) 2014, 2015, 2016 Paul Boddie <paul@boddie.org.uk>
4.10 +
4.11 +This program is free software; you can redistribute it and/or modify it under
4.12 +the terms of the GNU General Public License as published by the Free Software
4.13 +Foundation; either version 3 of the License, or (at your option) any later
4.14 +version.
4.15 +
4.16 +This program is distributed in the hope that it will be useful, but WITHOUT
4.17 +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
4.18 +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
4.19 +details.
4.20 +
4.21 +You should have received a copy of the GNU General Public License along with
4.22 +this program. If not, see <http://www.gnu.org/licenses/>.
4.23 +"""
4.24 +
4.25 +from os.path import abspath, split
4.26 +import sys
4.27 +
4.28 +# Find the modules.
4.29 +
4.30 +try:
4.31 + import imiptools
4.32 +except ImportError:
4.33 + parent = abspath(split(split(__file__)[0])[0])
4.34 + if split(parent)[1] == "imip-agent":
4.35 + sys.path.append(parent)
4.36 +
4.37 +from imiptools import config
4.38 +from imiptools.data import Object
4.39 +from imiptools.stores import get_store, get_publisher, get_journal
4.40 +
4.41 +def copy_store(from_store, from_journal, to_store, to_journal):
4.42 +
4.43 + """
4.44 + Copy stored information from the specified 'from_store' and 'from_journal'
4.45 + to the specified 'to_store' and 'to_journal' respectively.
4.46 + """
4.47 +
4.48 + # For each user...
4.49 +
4.50 + for user in from_store.get_users():
4.51 +
4.52 + # Copy requests.
4.53 +
4.54 + to_store.set_requests(user, from_store.get_requests(user))
4.55 +
4.56 + # Copy events, both active and cancellations.
4.57 +
4.58 + for dirname in (None, "cancellations"):
4.59 +
4.60 + # Get event, recurrence information.
4.61 +
4.62 + for uid, recurrenceid in from_store.get_all_events(user, dirname=dirname):
4.63 + d = from_store.get_event(user, uid, recurrenceid, dirname=dirname)
4.64 + if d:
4.65 + to_store.set_event(user, uid, recurrenceid, Object(d).to_node())
4.66 + if dirname == "cancellations":
4.67 + to_store.cancel_event(user, uid, recurrenceid)
4.68 + else:
4.69 + print >>sys.stderr, "Event for %s with UID %s and RECURRENCE-ID %s not found in %s" % (
4.70 + (user, uid, recurrenceid or "null", dirname or "active events"))
4.71 +
4.72 + # Copy counter-proposals.
4.73 +
4.74 + if dirname is None:
4.75 + for other in from_store.get_counters(user, uid, recurrenceid):
4.76 + d = from_store.get_counter(user, other, uid, recurrenceid)
4.77 + if d:
4.78 + to_store.set_counter(user, other, Object(d).to_node(), uid, recurrenceid)
4.79 + else:
4.80 + print >>sys.stderr, "Counter-proposal for %s with UID %s and RECURRENCE-ID %s not found in %s" % (
4.81 + (user, uid, recurrenceid or "null", dirname or "active events"))
4.82 +
4.83 + # Copy free/busy information for the user.
4.84 +
4.85 + to_store.set_freebusy(user, from_store.get_freebusy(user))
4.86 +
4.87 + # Copy free/busy information for other users.
4.88 +
4.89 + for other in from_store.get_freebusy_others(user):
4.90 + to_store.set_freebusy_for_other(user, from_store.get_freebusy_for_other(user, other), other)
4.91 +
4.92 + # Copy free/busy offers.
4.93 +
4.94 + to_store.set_freebusy_offers(user, from_store.get_freebusy_offers(user))
4.95 +
4.96 + # For each quota group...
4.97 +
4.98 + for quota in from_journal.get_quotas():
4.99 +
4.100 + # Copy quota limits.
4.101 +
4.102 + for user_group, limit in from_journal.get_limits(quota).items():
4.103 + to_journal.set_limit(quota, user_group, limit)
4.104 +
4.105 + # Copy group mappings.
4.106 +
4.107 + for store_user, user_group in from_journal.get_groups(quota).items():
4.108 + to_journal.set_group(quota, store_user, user_group)
4.109 +
4.110 + # Copy journal details.
4.111 +
4.112 + for group in from_journal.get_quota_users(quota):
4.113 + to_journal.set_entries(quota, group, from_journal.get_entries(quota, group))
4.114 +
4.115 + # Copy individual free/busy details.
4.116 +
4.117 + for store_user in from_journal.get_freebusy_users(quota):
4.118 + to_journal.set_freebusy(store_user, from_journal.get_freebusy(store_user))
4.119 +
4.120 +# Main program.
4.121 +
4.122 +if __name__ == "__main__":
4.123 +
4.124 + # Interpret the command line arguments.
4.125 +
4.126 + from_store_args = []
4.127 + to_store_args = []
4.128 + l = ignored = []
4.129 +
4.130 + for arg in sys.argv[1:]:
4.131 + if arg in ("-t", "--to"):
4.132 + l = to_store_args
4.133 + elif arg in ("-f", "--from"):
4.134 + l = from_store_args
4.135 + else:
4.136 + l.append(arg)
4.137 +
4.138 + if len(from_store_args) not in (0, 3) or len(to_store_args) != 3:
4.139 + print >>sys.stderr, """\
4.140 +Usage: %s \\
4.141 + [ ( -f | --from ) <store type> <store directory> <journal directory> ] \\
4.142 + ( -t | --to ) <store type> <store directory> <journal directory>
4.143 +
4.144 +Need details of a destination store indicated by the -t or --to option.
4.145 +In addition, details of a source store may be indicated by the -f or --from
4.146 +option; otherwise, the currently-configured store is used.
4.147 +""" % split(sys.argv[0])[1]
4.148 + sys.exit(1)
4.149 +
4.150 + # Override defaults if indicated.
4.151 +
4.152 + getvalue = lambda value, pos=0, default=None: value and value[pos] or default
4.153 +
4.154 + from_store_type = getvalue(from_store_args, 0, config.STORE_TYPE)
4.155 + from_store_dir = getvalue(from_store_args, 1)
4.156 + from_journal_dir = getvalue(from_store_args, 2)
4.157 +
4.158 + to_store_type, to_store_dir, to_journal_dir = to_store_args
4.159 +
4.160 + # Obtain store-related objects.
4.161 +
4.162 + from_store = get_store(from_store_type, from_store_dir)
4.163 + from_journal = get_journal(from_store_type, from_journal_dir)
4.164 +
4.165 + to_store = get_store(to_store_type, to_store_dir)
4.166 + to_journal = get_journal(to_store_type, to_journal_dir)
4.167 +
4.168 + # Process the store.
4.169 +
4.170 + copy_store(from_store, from_journal, to_store, to_journal)
4.171 +
4.172 +# vim: tabstop=4 expandtab shiftwidth=4
5.1 --- a/tools/install.sh Thu Apr 21 23:17:35 2016 +0200
5.2 +++ b/tools/install.sh Thu Apr 21 23:20:57 2016 +0200
5.3 @@ -103,7 +103,7 @@
5.4
5.5 # Tools
5.6
5.7 -TOOLS="fix.sh init.sh init_user.sh make_freebusy.py set_quota_limit.py update_quotas.py update_scheduling_modules.py"
5.8 +TOOLS="copy_store.py fix.sh init.sh init_user.sh make_freebusy.py set_quota_limit.py update_quotas.py update_scheduling_modules.py"
5.9
5.10 if [ ! -e "$INSTALL_DIR/tools" ]; then
5.11 mkdir -p "$INSTALL_DIR/tools"