def delete_race_and_ratings(race, *args, **kwargs): from rated_tree_modifications import make_temporary_clone # ^ here to aviod cyclic import ^ assert type(race).__name__ == 'Race' log = logging.getLogger('structure_modification') log_prefix = 'race.delete(): id={}'.format(race.id) log.debug('{} before flock'.format(log_prefix)) with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): log.debug('{} trnsctn start'.format(log_prefix)) try: with transaction.atomic(): for overall_node in race.sr_overall.all(): make_temporary_clone(overall_node, delete_original=True) # Linked Race_by_param, User_overall, User_by_param and corresponding *_update # instances will be deleted by CASCADE mechanism for group in race.group_set.all(): group.delete() # Linked Group_new, Primary and User_review instances will be deleted # by CASCADE mechanism super(type(race), race).delete(*args, **kwargs) log.debug('{} trnsctn end'.format(log_prefix)) except Exception as e: log.error('{} Unexpected error: {}'.format(log_prefix, repr(e)), exc_info=True) raise else: log.info('{} OK'.format(log_prefix))
def remove_series(request, organizer_id, series_id): organizer = get_object_or_404(models.Organizer, pk=organizer_id) if 'btnRemoveSeries' in request.POST: series = get_object_or_404(models.Series, pk=series_id) if series.organizer != organizer: messages.warning(request, u'У серии {} (id {}) и так не указан организатор {} (id {}).'.format( series.name, series_id, series.organizer.name, series.organizer.id)) else: log = logging.getLogger('structure_modification') log_prefix = 'remove_series {} from organizer {} by user {}.'.format(series.id, organizer_id, request.user.id) log.debug('{} before flock'.format(log_prefix)) log_exc_info = False with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): log.debug('{} trnsctn start'.format(log_prefix)) try: with transaction.atomic(): change_parent(series, models.Organizer.objects.fake_object) # ^ to adapt the starrating data) ^ series.organizer_id = models.FAKE_ORGANIZER_ID series.save() models.log_obj_create(request.user, series, models.ACTION_UPDATE, field_list=['organizer']) log.debug('{} trnsctn end'.format(log_prefix)) except (UpdatedRecordExistsError, AssertionError) as e: error_msg = repr(e) if isinstance(e, AssertionError): log_exc_info = True else: error_msg = None if error_msg is None: log.info('{} OK'.format(log_prefix)) messages.success(request, u'Серия «{}» (id {}) успешно отвязана от этого организатора.'.format(series.name, series_id)) else: log.error('{} {}'.format(log_prefix, error_msg), exc_info=log_exc_info) messages.warning(request, u'Не удалось отвязать серию «{}» (id {}) от этого организатора ({}).'.format(series.name, series_id, error_msg)) return redirect(organizer)
def create_all_zero_records_for_new_groups( method_ids=(), only_one_group=False, log=None): # print "create_all_zero_records_for_new_groups", method_ids assert isinstance(method_ids, (list, tuple)) if log: pid = getpid() log.debug( 'create_all_zero_records_for_new_groups[{}] start: methods_ids={}, only_one_group={},' .format( pid, method_ids, 1 if only_one_group else 0, )) if not method_ids: method_ids = tuple(get_actual_methods()) if log: log.debug('actual methods: {}'.format(method_ids)) if log: group_id_list = [] MAX_LIST_LENGTH = 100 group_count = 0 for group_id in Group_new.objects.all().values_list('id', flat=True): with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): with transaction.atomic(): for m_id in method_ids: mk_aggr_structure_for_group(group_id, method_id=m_id) Group_new.objects.filter(id_id=group_id).delete() result = True group_count += 1 if log: if group_count <= MAX_LIST_LENGTH: group_id_list.append(group_id) if only_one_group: break if log: log.debug( 'create_all_zero_records_for_new_groups[{}] result: group_count={}' .format( pid, group_count, )) if group_count > 0: log.info('processed groups: {}{}'.format( ' '.join([str(x) for x in group_id_list]), ' ... {}'.format(group_id) if len(group_id_list) == MAX_LIST_LENGTH and group_id != group_id_list[-1] else '')) return group_count
def process_update_record(obj): """ Requires: - на всех предыдущих уровнях уже посчитаны основые значения, _updated удалены - правильные by_param-значения для данного уровня уже записаны в _by_param_updated - если для overall данного уровня направление child - то их правильные значения тоже уже записаны в _overall_updated иначе, в _overall_updated могут (должны?) быть записаны NULL ДОДУМАЙ: user_count всегда вычисляется в направлении child! """ def copy_from_by_param_updated(overall_id, parameters): # TODO: копировать поля выборочно. sub_filter = by_param_updated_model.objects.filter(pk=OuterRef('pk')) by_param_model.objects.all().filter( overall_id=overall_id, parameter_id__in=parameters, ).update( sum_int=Subquery(sub_filter.values('sum_int')), sum_float=Subquery(sub_filter.values('sum_float')), user_count=Subquery(sub_filter.values('user_count')), weight=Subquery(sub_filter.values('weight')), ) def copy_from_overall_updated(overall_id): # TODO: копировать поля выборочно sub_filter = overall_updated_model.objects.filter(pk=OuterRef('pk')) # print type(overall_id) overall_model.objects.all().filter(pk=overall_id).update( sum_int=Subquery(sub_filter.values('sum_int')), sum_float=Subquery(sub_filter.values('sum_float')), user_count=Subquery(sub_filter.values('user_count')), weight=Subquery(sub_filter.values('weight')), ) def delete_overall_updated(overall_id): overall_updated_model.objects.filter(pk=overall_id).delete() def delete_by_param_updated(overall_id): by_param_updated_model.objects.filter( id__overall_id=overall_id).delete() def get_as_values(qs, fields=()): assert len(qs) == 1 return qs.values(*fields)[0] ######################## # End of sub-functions # ######################## assert isinstance(obj, Overall_updated_abstract_model) print "process_update_record", obj level = level_of_model(type(obj)) print "level=", level has_parent = (level + 1) in LEVEL_NO_TO_NAME ### Собираем модели и ids overall_updated_model = type(obj) overall_model = get_base_model(type(obj)) by_param_updated_model = get_dependent_model(type(obj)) by_param_model = get_base_model(by_param_updated_model) ov_id = obj.pk method_spec_this = get_methods_specification()[obj.id.method_id][level] if has_parent: overall_parent_model = type(obj.id.parent) by_param_parent_model = get_dependent_model(overall_parent_model) overall_parent_updated_model = get_upd_model(overall_parent_model) by_param_parent_updated_model = get_upd_model(by_param_parent_model) method_spec_parent = get_methods_specification()[obj.id.method_id][ level + 1] """ Надо в рамках транзакции * Вычисления на текущем уровне - by_param уже готов в _updated, вычислять не надо [НЕТ bp_current] - overall_updated: Если направление child, то уже готов в _updated, вычислять не надо Иначе их надо обновить на основе by_param_updated текущего уровня и результаты записать в _updated. [ov_current] * Вычисления на уровне parent - by_param: обновить parent _updated.parent на основе текущего _update и записать результаты в parent _upated модель. [pb_parent] - overall: - если направление указано child, аналогично предыдущему обновить parent _updated на основе текущего _update и записать результаты в parent _upated модель. - иначе создать parent _updated модель с NULL-значениями. Это все - [ov_parent] * скопировать by_param_updated и overall_updated в основную модель [copy_updated] * удалить by_param_updated и overall_updated (в любом порядке) [delete_updated] Зависимости: [delete_updated ov] after [copy_updated ov] after [ov_parent] after [ov_current] [delete_updated bp] after [copy_updated bp] after [bp_parent] Эти две цепочки могут выполнятся параллельно (зависимость ov_parent от bp_parent возможна, но в этом случае реальные вычисления происходят на шаге, соответсвующим след. уровню. Поэтому здесь эту зависимость можно игнорировать) """ ##### # считываем данные, с помощью которых будем апдейтить bp_data_fields = ('user_count', 'weight', method_spec_this['by_param_field']) ov_data_fields = ('user_count', 'weight', method_spec_this['overall_field']) if has_parent: bp_data_parent_fields = ('user_count', 'weight', method_spec_parent['by_param_field']) ov_data_parent_fields = ('user_count', 'weight', method_spec_parent['overall_field']) bp_new_data = queryset_to_dict( by_param_updated_model.objects.filter(id__overall_id=ov_id).values( 'id__parameter_id', *bp_data_fields), 'id__parameter_id') par_list = tuple(bp_new_data.keys()) by_param_qs = by_param_model.objects.filter(overall_id=ov_id, parameter_id__in=par_list) bp_old_data = queryset_to_dict( by_param_qs.values('parameter_id', *bp_data_fields), 'parameter_id') assert tuple(bp_old_data.keys()) == par_list if has_parent: bp_parent_records = queryset_to_dict( obj.id.parent.by_param.all().filter(parameter_id__in=par_list), 'parameter_id' ) # will be a dict of model instances indexed by parameter id overall_qs = overall_model.objects.filter(pk=ov_id) ov_old_data = get_as_values(overall_qs, ov_data_fields) ov_new_data = get_as_values( overall_updated_model.objects.filter(id__pk=ov_id), ov_data_fields + ('to_delete', ), ) # print "before transaction" with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): with transaction.atomic(): # overall sequence ''' calc_overall_current( old_data=bp_old_data, new_data=bp_new_data, bp_sum_field_name=method_spec_this['by_param_field'], ov_sum_field_name=method_spec_this['overall_field'], calc_method=method_spec_this['overall_spec'], ov_record=obj, upd_model=overall_parent_updated_model, direction=method_spec_this['overall_dir'], ) ''' if has_parent: calc_overall_parent( old_data=ov_old_data, new_data=ov_new_data, current_sum_field_name=method_spec_this['overall_field'], parent_sum_field_name=method_spec_parent['overall_field'], calc_method=method_spec_parent['overall_spec'], parent_record=obj.id.parent, upd_model=overall_parent_updated_model, direction=method_spec_parent['overall_dir']) copy_from_overall_updated(ov_id) delete_overall_updated(ov_id) # by_param sequence if has_parent: calc_by_param_parent( old_data=bp_old_data, new_data=bp_new_data, current_sum_field_name=method_spec_this['by_param_field'], parent_sum_field_name=method_spec_parent['by_param_field'], calc_method=method_spec_parent['by_param_spec'], parent_records=bp_parent_records, upd_model=by_param_parent_updated_model, ) copy_from_by_param_updated(ov_id, par_list) delete_by_param_updated(ov_id) if ov_new_data['to_delete']: # assert all_zero(ov_new_data) # TODO # assert all_zero(bp_new_data) by_param_qs.delete() overall_qs.delete()
def organizer_delete(request, organizer_id): organizer = get_object_or_404(models.Organizer, pk=organizer_id) has_dependent_objects = organizer.has_dependent_objects() ok_to_delete = False if 'frmForOrganizer_submit' in request.POST: form = forms.ForOrganizerForm(request.POST, auto_id='frmForOrganizer_%s') if form.is_valid(): if has_dependent_objects: organizer_for = form.cleaned_data['organizer'] if organizer_for != organizer: ok_to_delete = True else: messages.warning(request, u'Нельзя заменить организатора на его самого.') else: # There are no dependent objects of the organizer, so we just delete it ok_to_delete = True else: messages.warning(request, u'Организатор не удалён. Пожалуйста, исправьте ошибки в форме.') else: form = None messages.warning(request, u'Вы не указали город для удаления.') if ok_to_delete: # NOT TESTED ! if has_dependent_objects: organizer_for_id = organizer_for.id else: organizer_for_id = 0 organizer_for = None log = logging.getLogger('structure_modification') log_prefix = 'organizer_delete: organizer {}->{}, by user {}.'.format( organizer_id, organizer_for_id, request.user.id ) log_exc_info = False oranizer_name_full = organizer.name_full() log.debug('{} before flock'.format(log_prefix)) with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): try: with transaction.atomic(): if has_dependent_objects: update_organizer(request, organizer, organizer_for) models.log_obj_delete(request.user, organizer) organizer.delete() log.debug('{} trnsctn end'.format(log_prefix)) except (UpdatedRecordExistsError, AssertionError) as e: error_msg = repr(e) if isinstance(e, AssertionError): log_exc_info = True except Exception as e: log.error('{} Unexpected error: {}'.format(log_prefix, repr(e)), exc_info=True) raise else: error_msg = None if error_msg is None: log.info('{} OK'.format(log_prefix)) messages.success(request, u'Организатор «{}» успешно удалён.'.format(organizer_name_full)) else: log.error('{} {}'.format(log_prefix, error_msg), exc_info=log_exc_info) messages.warning( request, u'Не удалось удалить организатора «{}» ({}).'.format( organizer_name_full, error_msg ) ) if has_dependent_objects: return redirect(organizer_for.get_editor_url()) else: return redirect('editor:organizer_create') return organizer_details(request, organizer_id=organizer_id)
def series_delete(request, series_id): series = get_object_or_404(models.Series, pk=series_id) context, has_rights, target = check_rights(request, series=series) if not has_rights: return target has_dependent_objects = series.has_dependent_objects() ok_to_delete = False if (request.method == 'POST') and request.POST.get('frmForSeries_submit', False): form = forms.ForSeriesForm(request.POST, auto_id='frmForSeries_%s') if form.is_valid(): if has_dependent_objects: new_series_id = models.int_safe( request.POST.get('new_series_id', 0)) if new_series_id: if new_series_id != series.id: new_series = models.Series.objects.filter( pk=new_series_id).first() if new_series: ok_to_delete = True else: messages.warning( request, u'Серия, на которую нужно заменить текущую, не найдена.' ) else: messages.warning(request, u'Нельзя заменить серию на неё же.') else: messages.warning( request, u'Серия, на которую нужно заменить текущую, не указана.' ) else: # There are no events in the series, so we just delete it ok_to_delete = True else: messages.warning( request, u"Серия не удалена. Пожалуйста, исправьте ошибки: {}".format( form.errors)) else: form = forms.ForSeriesForm(auto_id='frmForSeries_%s') messages.warning(request, u"Вы не указали серию для удаления.") if ok_to_delete: if not has_dependent_objects: new_series_id = 0 new_series = None log = logging.getLogger('structure_modification') log_prefix = 'series_delete: series {}->{}, by user {}.'.format( series_id, new_series_id, request.user.id) log_exc_info = False log.debug('{} before flock'.format(log_prefix)) with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): try: with transaction.atomic(): if has_dependent_objects: update_series(request, series, new_series) transfer_children_before_node_deletion(series, new_series) models.log_obj_delete(request.user, series) series.delete() log.debug('{} trnsctn end'.format(log_prefix)) except (UpdatedRecordExistsError, AssertionError) as e: error_msg = repr(e) if isinstance(e, AssertionError): log_exc_info = True except Exception as e: log.error('{} Unexpected error: {}'.format( log_prefix, repr(e)), exc_info=True) raise else: error_msg = None if error_msg is None: log.info('{} OK'.format(log_prefix)) messages.success(request, u'Серия «{}» успешно удалена.'.format(series)) else: log.error('{} {}'.format(log_prefix, error_msg), exc_info=log_exc_info) messages.warning( request, u'Не удалось удалить серию «{}» ({}).'.format( series, error_msg)) if has_dependent_objects: return redirect(new_series) else: return redirect('results:races') return series_details(request, series_id=series_id, series=series, frmForSeries=form)
def event_delete(request, event_id): event = get_object_or_404(models.Event, pk=event_id) context, has_rights, target = check_rights(request, event=event) if not has_rights: return target has_dependent_objects = event.has_dependent_objects() ok_to_delete = False if 'frmForEvent_submit' in request.POST: form = forms.ForEventForm(request.POST, auto_id='frmForEvent_%s') if form.is_valid(): if has_dependent_objects: new_event_id = models.int_safe(request.POST.get('new_event_id', 0)) if new_event_id: if new_event_id != event.id: new_event = models.Event.objects.filter(pk=new_event_id).first() if new_event: ok_to_delete = True else: messages.warning(request, u'Забег, на который нужно заменить текущий, не найден.') else: messages.warning(request, u'Нельзя заменить забег на себя же.') else: messages.warning(request, u'Забег, на который нужно заменить текущий, не указан.') else: # There are no races in the event, so we just delete it ok_to_delete = True else: messages.warning(request, u"Забег не удалён. Пожалуйста, исправьте ошибки в форме.") else: form = None messages.warning(request, u"Вы не указали забег для удаления.") if ok_to_delete: if not has_dependent_objects: new_event_id = 0 new_event = None log = logging.getLogger('structure_modification') log_prefix = 'event_delete: event {}->{}, by user {}.'.format(event_id, new_event_id, request.user.id) log.debug('{} before flock'.format(log_prefix)) log_exc_info = False with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): event_name = event.name series = event.series log.debug('{} trnsctn start'.format(log_prefix)) try: with transaction.atomic(): if has_dependent_objects: update_event(request, event, new_event) log.debug('{} 1'.format(log_prefix)) transfer_children_before_node_deletion(event, new_event) log.debug('{} 2'.format(log_prefix)) models.log_obj_delete(request.user, event) log.debug('{} 3'.format(log_prefix)) start_date = event.start_date event.delete() log.debug('{} 4'.format(log_prefix)) update_events_count() log.debug('{} 5'.format(log_prefix)) update_course_records(series) if event.series.is_russian_parkrun(): log.debug('{} 6'.format(log_prefix)) prev_event = series.event_set.filter(start_date__lt=start_date).order_by('-start_date').first() if prev_event: _, n_fixed_parkruns = views_parkrun.fix_parkrun_numbers(correct_event=prev_event) messages.success(request, u'Исправлена нумерация у {} паркран{} после удалённого'.format(n_fixed_parkruns, results_util.plural_ending_new(n_fixed_parkruns, 1))) log.debug('{} trnsctn end'.format(log_prefix)) except (UpdatedRecordExistsError, AssertionError) as e: error_msg = repr(e) if isinstance(e, AssertionError): log_exc_info = True except Exception as e: log.error('{} Unexpected error: {}'.format(log_prefix, repr(e)), exc_info=True) raise else: error_msg = None if error_msg is None: log.info('{} OK'.format(log_prefix)) messages.success(request, u'Забег «{}» из серии «{}» успешно удалён.'.format(event_name, series)) else: log.error('{} {}'.format(log_prefix, error_msg), exc_info=log_exc_info) messages.warning( request, u'Не удалось удалить забег «{}» из серии «{}»<br />({}).'.format( event_name, series, error_msg ) ) return redirect(event.series) return event_details(request, event_id=event_id, event=event, frmForEvent=form)
def event_change_series(request, event_id): event = get_object_or_404(models.Event, pk=event_id) context, has_rights, target = check_rights(request, event=event) if not has_rights: return target ok_to_move = False if 'frmForSeries_submit' in request.POST: form = forms.ForSeriesForm(request.POST, auto_id='frmForSeries_%s') if form.is_valid(): new_series_id = models.int_safe(request.POST.get('new_series_id', 0)) if new_series_id: if new_series_id != event.series.id: new_series = models.Series.objects.filter(pk=new_series_id).first() if new_series: ok_to_move = True else: messages.warning(request, u'Серия, в которую нужно переместить забег, не найдена.') else: messages.warning(request, u'Забег уже находится в этой серии.') else: messages.warning(request, u'Серия, на которую нужно заменить текущую, не указана.') else: messages.warning(request, u"Серия не заменена. Пожалуйста, исправьте ошибки в форме.") else: form = None messages.warning(request, u"Вы не указали новую серию.") if ok_to_move: old_series = event.series log = logging.getLogger('structure_modification') log_prefix = 'event_change_series: event {}, series {}->{}, by user {}.'.format(event_id, old_series.id, new_series.id, request.user.id) log.debug('{} before flock'.format(log_prefix)) log_exc_info = False with Flock_mutex(LOCK_FILE_FOR_RATED_TREE_MODIFICATIONS): log.debug('{} trnsctn start'.format(log_prefix)) try: with transaction.atomic(): change_parent(event, new_series) # to adapt the starrating data event.series = new_series event.save() for race in event.race_set.all(): race.clean() race.save() log_change_event_series(request.user, event) update_course_records(old_series) update_course_records(event.series) log.debug('{} trnsctn end'.format(log_prefix)) except (UpdatedRecordExistsError, AssertionError) as e: error_msg = repr(e) if isinstance(e, AssertionError): log_exc_info = True else: error_msg = None if error_msg is None: log.info('{} OK'.format(log_prefix)) messages.success(request, u'Забег «{}» успешно перемещён из серии «{}» в серию «{}».'.format(event, old_series, event.series)) else: log.error('{} {}'.format(log_prefix, error_msg), exc_info=log_exc_info) messages.warning(request, u'Не удалось переместить забег «{}» из серии «{}» в серию «{}»<br />({}).'.format(event, old_series, event.series, error_msg)) return redirect(event.get_editor_url()) return event_details(request, event_id=event_id, event=event, frmForSeries=form)