# HG changeset patch # User Paul Boddie # Date 1505246964 -7200 # Node ID 64aacdbd40e25fd6cf8b1c0d33c7e48f0dcefc04 # Parent 694fd9694bdf77f5824c713d15e6ac260996033f# Parent 47093230ddb8418efcb4b46fc70ad01925a7cc99 Merged changes from concurrent branch. diff -r 694fd9694bdf -r 64aacdbd40e2 imipweb/event.py --- a/imipweb/event.py Tue Sep 12 20:28:08 2017 +0200 +++ b/imipweb/event.py Tue Sep 12 22:09:24 2017 +0200 @@ -65,7 +65,8 @@ without notification. """ - return (self.can_edit_recurrence(recurrence) or not self.is_organiser()) and \ + return (not self.is_organiser() or + self.can_edit_recurrence(recurrence)) and \ recurrence.origin != "RRULE" def can_edit_recurrence(self, recurrence): @@ -87,6 +88,7 @@ notification. """ + attendee = get_uri(attendee) return self.can_edit_attendee(attendee) or attendee == self.user and self.is_organiser() def can_edit_attendee(self, attendee): @@ -412,7 +414,7 @@ remove_type = self.can_remove_attendee(attendee_uri) and "submit" or "checkbox" self.control("remove", remove_type, str(i), - str(i) in self.get_state("remove", list), + attendee in self.get_state("remove", list), id="remove-%d" % i, class_="remove") page.label(_("Remove"), for_="remove-%d" % i, class_="remove") @@ -510,7 +512,7 @@ remove_type = self.can_remove_recurrence(period) and "submit" or "checkbox" self.control("recur-remove", remove_type, str(index), - str(index) in self.get_state("recur-remove", list), + period in self.get_state("recur-remove", list), id="recur-remove-%d" % index, class_="remove") page.label(_("Remove"), for_="recur-remove-%d" % index, class_="remove") @@ -852,10 +854,9 @@ # modified period information. # NOTE: Currently, rules are not updated. - to_unschedule, to_exclude = self.get_removed_periods(periods) + active_periods, to_unschedule, to_exclude = self.get_removed_periods(periods) + periods = set(periods) - active_periods = [p for p in periods if not p.replaced] - changed = self.obj.set_period(period) or changed changed = self.obj.set_periods(periods) or changed @@ -879,7 +880,7 @@ # Obtain any new participants and those to be removed. attendees = self.get_current_attendees() - removed = self.get_removed_attendees(attendees) + removed = self.get_removed_attendees() added, to_cancel = self.update_attendees(attendees, removed) single_user = not attendees or uri_values(attendees) == [self.user] @@ -1003,6 +1004,19 @@ periods.append(p.as_event_period(i)) return periods + def get_active_periods(self, periods): + + "Return a mapping of non-replaced periods to counts, given 'periods'." + + active_periods = {} + for p in periods: + if not p.replaced: + if not active_periods.has_key(p): + active_periods[p] = 1 + else: + active_periods[p] += 1 + return active_periods + # Access to form-originating object information. def get_main_period_from_page(self): @@ -1051,6 +1065,50 @@ attendee_map = self.obj.get_value_map("ATTENDEE") return [get_verbose_address(value, attendee_map.get(value)) for value in attendees] + def filter_duplicates(self, l): + + """ + Return collection 'l' filtered for duplicate values, retaining the given + element ordering. + """ + + s = set() + f = [] + + for value in l: + if value not in s: + s.add(value) + f.append(value) + + return f + + def remove_from_collection(self, l, indexes, fn): + + """ + Remove from collection 'l' all values having 'indexes' where 'fn' + applied to each referenced value returns a true value. Values where 'fn' + returns a false value are added to a list of deferred removals which is + returned. + """ + + still_to_remove = [] + correction = 0 + + for i in indexes: + try: + i = int(i) - correction + value = l[i] + except (IndexError, ValueError): + continue + + if fn(value): + del l[i] + correction += 1 + else: + still_to_remove.append(value) + + return still_to_remove + def update_attendees_from_page(self): "Add or remove attendees. This does not affect the stored object." @@ -1059,7 +1117,9 @@ attendees = self.get_attendees_from_page() - if args.has_key("add"): + add = args.has_key("add") + + if add: attendees.append("") # Add attendees suggested in counter-proposals. @@ -1078,24 +1138,15 @@ # Only actually remove attendees if the event is unsent, if the attendee # is new, or if it is the current user being removed. - if args.has_key("remove"): - still_to_remove = [] - correction = 0 + remove = args.has_key("remove") - for i in args["remove"]: - try: - i = int(i) - correction - attendee = attendees[i] - except (IndexError, ValueError): - continue + if remove: + still_to_remove = self.remove_from_collection(attendees, + args["remove"], self.can_remove_attendee) + self.set_state("remove", still_to_remove) - if self.can_remove_attendee(get_uri(attendee)): - del attendees[i] - correction += 1 - else: - still_to_remove.append(str(i)) - - self.set_state("remove", still_to_remove) + if add or add_suggested or remove: + attendees = self.filter_duplicates(attendees) return attendees @@ -1107,7 +1158,9 @@ recurrences = self.get_recurrences_from_page() - if args.has_key("recur-add"): + add = args.has_key("recur-add") + + if add: period = self.get_current_main_period().as_form_period() period.origin = "RDATE" recurrences.append(period) @@ -1115,24 +1168,15 @@ # Only actually remove recurrences if the event is unsent, or if the # recurrence is new, but only for explicit recurrences. - if args.has_key("recur-remove"): - still_to_remove = [] - correction = 0 + remove = args.has_key("recur-remove") - for i in args["recur-remove"]: - try: - i = int(i) - correction - recurrence = recurrences[i] - except (IndexError, ValueError): - continue + if remove: + still_to_remove = self.remove_from_collection(recurrences, + args["recur-remove"], self.can_remove_recurrence) + self.set_state("recur-remove", still_to_remove) - if self.can_remove_recurrence(recurrence): - del recurrences[i] - correction += 1 - else: - still_to_remove.append(str(i)) - - self.set_state("recur-remove", still_to_remove) + if add or remove: + recurrences = self.filter_duplicates(recurrences) return recurrences @@ -1207,17 +1251,14 @@ self.get_stored_attendees or self.update_attendees_from_page, overwrite=True) - def get_removed_attendees(self, attendees): + def get_removed_attendees(self): """ - Return details of 'attendees' to be removed according to previously + Return details of attendees to be removed according to previously determined removal information. """ - removed = [] - for i in self.get_state("remove", list): - removed.append(attendees[int(i)]) - return removed + return self.get_state("remove", list) def get_removed_periods(self, periods): @@ -1227,21 +1268,33 @@ excluded). """ - to_unschedule = [] - to_exclude = [] + to_unschedule = set() + to_exclude = set() + + # Get all periods that are not replaced. - for i in self.get_state("recur-remove", list): - try: - period = periods[int(i)] - except (IndexError, ValueError): - continue + active_periods = self.get_active_periods(periods) + + for period in self.get_state("recur-remove", list): + active_periods[period] -= 1 if not self.can_edit_recurrence(period) and self.is_organiser(): - to_unschedule.append(period) + l = to_unschedule else: - to_exclude.append(period) + l = to_exclude + l.add(period) + + # Determine whether some periods are both removed and added. - return to_unschedule, to_exclude + remaining = [] + for period, n in active_periods.items(): + if n > 0: + remaining.append(period) + + to_unschedule.difference_update(remaining) + to_exclude.difference_update(remaining) + + return remaining, to_unschedule, to_exclude # Full page output methods.