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