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(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))