def get_conditional_alert_rows(domain): translated_rows = [] untranslated_rows = [] langs = get_language_list(domain) for rule in get_conditional_alerts_queryset_by_domain(domain): schedule = rule.get_messaging_rule_schedule() events = schedule.memoized_events send_frequency = ScheduleForm.get_send_frequency_by_ui_type( schedule.ui_type) # Custom schedules may have multiple events each with different content # Non-custom schedules may have multiple events (e.g., daily schedule sent MWF) but each # event will have identical content, so only include the first one if send_frequency not in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE): events = [events[0]] for event in events: common_columns = [rule.pk, rule.name] if UntranslatedConditionalAlertUploader.event_is_relevant(event): untranslated_rows.append(common_columns + [event.content.message.get('*', '')]) elif TranslatedConditionalAlertUploader.event_is_relevant(event): translated_rows.append( common_columns + [event.content.message.get(lang, '') for lang in langs]) return (translated_rows, untranslated_rows)
def save_rule_messages(self, rule, rows): schedule = rule.get_schedule() send_frequency = ScheduleForm.get_send_frequency_by_ui_type( schedule.ui_type) # Iterate over rule's events if send_frequency not in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE): # For standard schedules, the rule may have multiple events (e.g., a daily rule run Mon and Tues # will have 2 events), but all events will have the same message, so we only need to look at the # first event. Since there's only one message, the user is free to move it between sheets. events = [rule.get_schedule().memoized_events[0]] allow_sheet_swap = True else: # For custom schedules, each event has its own message, so we need to look at all of them, and they may # be a mix of translated and untranslated. # If all messages are on one sheet: The user may update any or all and may move ALL of them to the # other sheet. We assume that the order of rows is the same as the rule's order of events. # If messages are split between sheets: The user may only make updates on one sheet (once the first # sheet is updated, the rule will begin processing, so the second sheet's updates will fail). Again, # we assume the order of rows matches the order of events (with the other sheet's rows interleaved). # The user may not move messages between sheets, since we can't match events to messages. events = rule.get_schedule().memoized_events allow_sheet_swap = len(rows) == len(events) message_dirty = False new_messages = [] row_index = 0 for index, event in enumerate(events): old_message = event.content.message new_message = copy(old_message) if self.event_is_relevant(event) or allow_sheet_swap: new_message = self.update_message(new_message, rows[row_index]) message_dirty = message_dirty or old_message != new_message row_index += 1 new_messages.append(new_message) if message_dirty: if any(True for message in new_messages for value in message.values() if not value): raise RuleUpdateError(_("Missing message")) { ScheduleForm.SEND_IMMEDIATELY: self._save_immediate_schedule, ScheduleForm.SEND_DAILY: self._save_daily_schedule, ScheduleForm.SEND_WEEKLY: self._save_weekly_schedule, ScheduleForm.SEND_MONTHLY: self._save_monthly_schedule, ScheduleForm.SEND_CUSTOM_IMMEDIATE: self._save_custom_immediate_schedule, ScheduleForm.SEND_CUSTOM_DAILY: self._save_custom_daily_schedule, }[send_frequency](schedule, new_messages) return message_dirty
def upload(self, workbook): self.msgs = [] success_count = 0 worksheet = workbook.get_worksheet(title=self.sheet_name) errors = self.get_worksheet_errors(worksheet) if errors: return errors # Most rules are represented by a single row, but rules with custom schedules have one row per event. # Read through the worksheet, grouping rows by rule id and caching rule definitions. condensed_rows = defaultdict(list) rules_by_id = {} for index, row in enumerate(worksheet, start=2): # one-indexed, plus header row if not row.get('id', None): self.msgs.append( (messages.error, _("Row {index} in '{sheet_name}' sheet is missing " "an id.").format(index=index, sheet_name=self.sheet_name))) continue if row['id'] in condensed_rows: # This is the 2nd (or 3rd, 4th, ...) row for a rule we've already seen condensed_rows[row['id']].append(row) continue try: rule = AutomaticUpdateRule.objects.get( pk=row['id'], domain=self.domain, workflow=AutomaticUpdateRule.WORKFLOW_SCHEDULING, deleted=False, ) except AutomaticUpdateRule.DoesNotExist: self.msgs.append(( messages.error, _("""Could not find rule for row {index} in '{sheet_name}' sheet, """ """with id {id}""").format(index=index, id=row['id'], sheet_name=self.sheet_name))) continue if rule.locked_for_editing: self.msgs.append(( messages.error, _("Row {index} in '{sheet_name}' sheet, with rule id {id}, " "is currently processing and cannot be updated.").format( index=index, id=row['id'], sheet_name=self.sheet_name))) continue if not isinstance( rule.get_messaging_rule_schedule().memoized_events[0]. content, SMSContent): self.msgs.append(( messages.error, _("Row {index} in '{sheet_name}' sheet, with rule id {id}, " "does not use SMS content.").format( index=index, id=row['id'], sheet_name=self.sheet_name))) continue rules_by_id[row['id']] = rule condensed_rows[row['id']].append(row) # Update the condensed set of rules for rule_id, rows in condensed_rows.items(): rule = rules_by_id[rule_id] schedule = rule.get_messaging_rule_schedule() send_frequency = ScheduleForm.get_send_frequency_by_ui_type( schedule.ui_type) if send_frequency in (ScheduleForm.SEND_CUSTOM_DAILY, ScheduleForm.SEND_CUSTOM_IMMEDIATE): # Check that user provided one row for each event in the custom schedule all_events = rule.get_messaging_rule_schedule().memoized_events expected = len( [e for e in all_events if self.event_is_relevant(e)]) actual = len(rows) if expected != actual and actual != len(all_events): self.msgs.append(( messages.error, _("Could not update rule with id {id} in '{sheet_name}' " "sheet: expected {expected} row(s) but found " "{actual}.").format(id=rule.id, sheet_name=self.sheet_name, expected=expected, actual=actual))) continue with transaction.atomic(): try: dirty = self.update_rule(rule, rows) except RuleUpdateError as e: self.msgs.append(( messages.error, _("Error updating rule with id {id} in '{sheet_name}' " "sheet: {detail}").format(id=rule.id, sheet_name=self.sheet_name, detail=str(e)))) continue if dirty: rule.save() initiate_messaging_rule_run(rule) success_count += 1 self.msgs.append( (messages.success, _("Updated {count} rule(s) in '{sheet_name}' sheet").format( count=success_count, sheet_name=self.sheet_name))) return self.msgs