def _try_to_reconcile(form_number, code, user): form_types, projects = get_forms() keyed_forms = Form.objects.filter(form_number=form_number, form_code=code) for f in keyed_forms: mixin(DataEntryForm, form_types[f.form_code]) usrs = [User.objects.get(id = uid) for uid in list(set([ f.user_id for f in keyed_forms ]))] keyed_numbers = [ len([f for f in keyed_forms if f.user_id == u.id ]) for u in usrs ] tf = DataEntryForm() reconcilable = len(filter(lambda n: n > 0, keyed_numbers)) >= tf.min_authors # Check if already has been reconciled rids = [f.reconciled_id for f in keyed_forms] # check if reconciled when it shouldn't be if not reconcilable and len(filter(None, rids)) > 0: unreconcile_forms(form_number, code) for form in keyed_forms: form.fields_dict = form.fields_dict() if reconcilable: done = len(filter(None, rids)) == len(keyed_forms) and len(set(rids)) == 1 good = True goodfields = dict(form_number = form_number) for fn in tf.field_names(): vals = [] for form in keyed_forms: if not form.fields_dict.has_key(fn): continue vals.append(form.fields_dict[fn]) vals = list(set([str(v) for v in vals])) if len(vals) == 1: goodfields[fn] = vals[0] if len(vals) > 1: good = False if good and not done: form = DataEntryForm(goodfields) if form.is_valid() and form.is_really_valid(goodfields): reconcile_forms(user, form_number, form) return True if not good and done: unreconcile_forms(form_number, code)
def reconcile_forms(user, form_number, form): existing = Reconciled.objects.filter( form_number = form.cleaned_data['form_number'], form_code = form.code) if existing: reconciled = existing[0] reconciled.user = user else: # Create new reconciled object, for this user reconciled = Reconciled( user = user, form_code = form.code, form_number = form.cleaned_data['form_number'], ) reconciled.save(using='default') form.break_caches() tf = DataEntryForm() # update the forms, and each field in the forms for f in Form.objects.filter(form_number=form_number, form_code=form.code): f.reconciled = reconciled f.save() fd = f.fields_dict() groomed = {} tf.groom_all(groomed, fd) fields = Field.objects.filter(form=f, updated__isnull=True) fs = {} for field in fields: fs[field.name] = field fieldstocreate = [] for field_name in form.field_names(): if field_name in fs: field = fs[field_name] if field.value != form.cleaned_data[field.name]: newfield = Field( user = user, form = f, name = field.name, value = form.cleaned_data[field.name] ) if groomed.has_key(field.name) and fd.has_key(field.name) and fd[field.name] == newfield.value: newfield.edit_type = groomed[field.name] newfield.save() field.updated = newfield field.save() form_updated = True else: fieldstocreate.append( Field( user = user, form = f, name = field_name, value = form.cleaned_data[field_name] ) ) form_updated = True if len(fieldstocreate): Field.objects.bulk_create(fieldstocreate) return reconciled
def reconcile(request, code, form_number): """Reconciliation screen""" form_types, projects = get_forms() referrer = request.META.get('HTTP_REFERER') all_forms = Form.objects.filter(form_number=form_number, form_code=code) usernames = dict([ (u.id, u.first_name or u.username) for u in User.objects.all()]) fts = list(set([f.form_code for f in all_forms])) if len(fts) != 1: assert False, "More than one form type for form %s !" % form_number # Do we have permission... code = fts[0] mixin(DataEntryForm, form_types[code]) if not request.user.has_perm("%s.reconciler" % DataEntryForm.project): return redirect(referrer) postform = None if request.method == 'POST': postform = DataEntryForm(request.POST) # postform.clean = postform.reconcile_clean if postform.is_valid() and postform.is_really_valid(): reconcile_forms(request.user, form_number, postform) referrer = request.POST['referrer'] if request.POST.has_key('import'): returnto = postform.importurl(form_number) elif referrer and referrer != 'None': returnto = referrer else: returnto = reverse('dataentry.views.show_forms', kwargs=dict(project = postform.project)) returnto = returnto + '#' + postform.cleaned_data['form_number'] return redirect(returnto) else: logging.debug(postform.errors) tf = DataEntryForm() for form in all_forms: form.fields_dict = form.fields_dict() form.groomed = {} tf.groom_all(form.groomed, form.fields_dict) fields = Field.objects.filter(form=form) form.fields_name_dict = {} for field in fields: if not field.name in form.fields_name_dict: form.fields_name_dict[field.name] = [] form.fields_name_dict[field.name].append(field) f = dict(form_number = form_number) info = dict() for fn in tf.field_names(): vals = [] groomed = False for form in all_forms: if not form.fields_dict.has_key(fn): continue vals.append(form.fields_dict[fn]) if form.groomed.has_key(fn): groomed = True def cmp_dates(a,b): return cmp(b.date_entered, a.date_entered) history = list( reduce( set.union, [set(frm.fields_name_dict[fn] if fn in frm.fields_name_dict else []) for frm in all_forms] ) ) history.sort(cmp_dates) history = [ dict( value = fi.value, user = usernames[fi.user_id], date_entered = fi.date_entered.strftime('%d %b %Y %H:%M'), ) for fi in history ] history = dedupe(history, lambda h: "**".join(h.values())) history_groomed = (len(list(set([ h['value'] for h in history ]))) > 1 and all_forms[0].reconciled != None) info[fn] = dict( groomed = groomed and len(list(set(vals))) == 1, vals = list(set([str(v) for v in vals])), frms = dict([ (form.id, form.fields_dict.get(fn,"")) for form in all_forms]), history = history, history_groomed = history_groomed, ) if len(set([str(v) for v in vals])) == 1: f[fn] = vals[0] else: f[fn] = '' # load up the comments comments = [] for fm in all_forms: comments.append(dict( comment = fm.comment, user = fm.user, )) if postform: form = postform errors = postform.errors else: form = DataEntryForm(f) form.is_really_valid(f) errors = form.errors importable = hasattr(form_types[code], 'imported_forms') and (code, form_number) not in form_types[code].imported_forms() extra = None if hasattr(form_types[code], 'ft_annotate'): extra = form.ft_annotate(form_number) return render_to_response("dataentry/reconcile.html", dict( form_number = form_number, forms = all_forms, form = form, reconciled = all_forms[0].reconciled, reconciling = True, comments = comments, info = simplejson.dumps(info), errors = simplejson.dumps(errors), referrer = referrer, importable = importable, menuname = 'dataentry', extra = extra, ), context_instance=RequestContext(request))
def form(request, code, form_id): form_types, projects = get_forms() if not code in form_types.keys(): return HttpResponseNotFound('<h1>No form type called %s</h1>' % code) all_forms = [] mixin(DataEntryForm, form_types[code]) if not request.user.has_perm("%s.data_entry" % DataEntryForm.project) and not request.user.is_staff: return redirect(list_projects) form_number = None secondtime = False groomed = dict() thisuser = None if request.method == 'POST': form = DataEntryForm(request.POST) if request.POST.has_key('delete'): delete_form(request.user, form_id) return redirect(reverse('dataentry.views.show_forms', kwargs=dict(project = form.project))) if form.is_valid(): # If the user is trying to enter, reconcile and import in # one step, validation and redirects are different. reconciler = request.user.has_perm("%s.reconciler" % DataEntryForm.project) or request.user.is_staff importing = reconciler and request.POST.has_key('import') and hasattr(form, 'importurl') if importing: validation_stage = 'reconcile' else: validation_stage = 'entry' # Do the groom check thing here... valid = False secondtime = True form_number = form.cleaned_data['form_number'] if not request.POST.has_key('secondtime') : form.groom_all(groomed) fd = form.cleaned_data if groomed: form = DataEntryForm(fd) valid = form.is_really_valid(fd, stage=validation_stage) else: valid = form.is_really_valid(stage=validation_stage) if not valid: secondtime = False logging.debug(form.errors) elif not groomed: if form_id: f = update_form(request.user, form_id, form) else: f = create_form(request.user, form, form.generate_form_number) success = _try_to_reconcile(f.form_number, f.form_code, request.user) if importing: if success: return redirect(form.importurl(f.form_number)) if reconciler: return redirect(reverse('dataentry.views.reconcile', kwargs=dict( code=f.form_code, form_number=f.form_number))) return redirect(reverse('dataentry.views.show_forms', kwargs=dict(project = form.project))) else: logging.debug(form.errors) else: if form_id: f = load_form(form_id) form_number = f['form_number'] all_forms = Form.objects.filter(form_number=form_number, form_code=code) for form in all_forms: if form.id == int(form_id): thisuser = form.user form.matched = True form = DataEntryForm(f) else: form = DataEntryForm() if form.generate_form_number: # Fill form_number with garbage for now; fix it in create_form f = {'form_number': 100000000 + int('0x' + request.session.session_key[11:17], 16)} form = DataEntryForm(f) allow_rename = True if not form_id: importable = hasattr(form_types[code], 'importurl') elif hasattr(form_types[code], 'imported_forms') and form_number != None: importable = (code, form_number) not in form_types[code].imported_forms() # Changing the form_number of a previously imported form # causes bad things to happen, don't allow it until this is # fixed. allow_rename = importable else: importable = False extra = None if form_id and hasattr(form_types[code], 'ft_annotate'): extra = form.ft_annotate(form_number) fts_q = reduce(lambda x,y: x|y, [Q(form_code=ft.code) for ft in form_types.values() if ft.project == form.project]) keyed_forms = Form.objects.filter(Q(user = request.user) & fts_q) nforms = keyed_forms.count() enable_save_button = request.user.is_staff or not form_id or thisuser == request.user info = dict() for fn in form.field_names() + ['form_comments']: info[fn] = dict() info[fn]['desc'] = ", ".join([ gr['description'] for gr in form.grooming_rules if fn in gr['fields']]) if groomed.has_key(fn): info[fn]['groomed'] = 1 return render_to_response('dataentry/form.html', dict( enable_save_button = enable_save_button, allow_rename = allow_rename, secondtime = secondtime, info = info, errors = simplejson.dumps(form.errors), form = form, form_id = form_id, all_forms = all_forms, number_of_forms = nforms, project_name = projects[form.project].name, importable = importable, menuname = 'dataentry', extra = extra, ), context_instance=RequestContext(request))