def merge(self, r, **attr): """ Merge form for two records @param r: the S3Request @param **attr: the controller attributes for the request @note: this method can always only be POSTed, and requires both "selected" and "mode" in post_vars, as well as the duplicate bookmarks list in session.s3 """ T = current.T session = current.session response = current.response output = dict() tablename = self.tablename # Get the duplicate bookmarks s3 = session.s3 DEDUPLICATE = self.DEDUPLICATE if DEDUPLICATE in s3: bookmarks = s3[DEDUPLICATE] if tablename in bookmarks: record_ids = bookmarks[tablename] # Process the post variables post_vars = r.post_vars mode = post_vars.get("mode") selected = post_vars.get("selected", "") selected = selected.split(",") if mode == "Inclusive": ids = selected elif mode == "Exclusive": ids = [i for i in record_ids if i not in selected] else: # Error ids = [] if len(ids) != 2: r.error(501, T("Please select exactly two records"), next=r.url(id=0, vars={})) # Get the selected records table = self.table query = (table._id == ids[0]) | (table._id == ids[1]) orderby = table.created_on if "created_on" in table else None rows = current.db(query).select(orderby=orderby, limitby=(0, 2)) if len(rows) != 2: r.error(404, current.ERROR.BAD_RECORD, next=r.url(id=0, vars={})) original = rows[0] duplicate = rows[1] # Prepare form construction formfields = [f for f in table if f.readable or f.writable] ORIGINAL, DUPLICATE, KEEP = self.ORIGINAL, self.DUPLICATE, self.KEEP keep_o = KEEP.o in post_vars and post_vars[KEEP.o] keep_d = KEEP.d in post_vars and post_vars[KEEP.d] trs = [] init_requires = self.init_requires for f in formfields: # Render the widgets oid = "%s_%s" % (ORIGINAL, f.name) did = "%s_%s" % (DUPLICATE, f.name) sid = "swap_%s" % f.name init_requires(f, original[f], duplicate[f]) if keep_o or not any((keep_o, keep_d)): owidget = self.widget(f, original[f], _name=oid, _id=oid) else: try: owidget = s3_represent_value(f, value=original[f]) except: owidget = s3_unicode(original[f]) if keep_d or not any((keep_o, keep_d)): dwidget = self.widget(f, duplicate[f], _name=did, _id=did) else: try: dwidget = s3_represent_value(f, value=duplicate[f]) except: dwidget = s3_unicode(duplicate[f]) # Swap button if not any((keep_o, keep_d)): swap = INPUT(_value="<-->", _class="swap-button", _id=sid, _type="button") else: swap = DIV(_class="swap-button") if owidget is None or dwidget is None: continue # Render label row label = f.label trs.append( TR(TD(label, _class="w2p_fl"), TD(), TD(label, _class="w2p_fl"))) # Append widget row trs.append( TR(TD(owidget, _class="mwidget"), TD(swap), TD(dwidget, _class="mwidget"))) # Show created_on/created_by for each record if "created_on" in table: original_date = original.created_on duplicate_date = duplicate.created_on if "created_by" in table: represent = table.created_by.represent original_author = represent(original.created_by) duplicate_author = represent(duplicate.created_by) created = T("Created on %s by %s") original_created = created % (original_date, original_author) duplicate_created = created % (duplicate_date, duplicate_author) else: created = T("Created on %s") original_created = created % original_date duplicate_created = created % duplicate_date else: original_created = "" duplicate_created = "" # Page title and subtitle output["title"] = T("Merge records") #output["subtitle"] = self.crud_string(tablename, "title_list") # Submit buttons if keep_o or not any((keep_o, keep_d)): submit_original = INPUT(_value=T("Keep Original"), _type="submit", _name=KEEP.o, _id=KEEP.o) else: submit_original = "" if keep_d or not any((keep_o, keep_d)): submit_duplicate = INPUT(_value=T("Keep Duplicate"), _type="submit", _name=KEEP.d, _id=KEEP.d) else: submit_duplicate = "" # Build the form form = FORM( TABLE( THEAD( TR( TH(H3(T("Original"))), TH(), TH(H3(T("Duplicate"))), ), TR( TD(original_created), TD(), TD(duplicate_created), _class="authorinfo", ), ), TBODY(trs), TFOOT(TR( TD(submit_original), TD(), TD(submit_duplicate), ), ), ), # Append mode and selected - required to get back here! hidden={ "mode": "Inclusive", "selected": ",".join(ids), }) output["form"] = form # Add RESET and CANCEL options output["reset"] = FORM(INPUT(_value=T("Reset"), _type="submit", _name="reset", _id="form-reset"), A(T("Cancel"), _href=r.url(id=0, vars={}), _class="action-lnk"), hidden={ "mode": mode, "selected": ",".join(ids) }) # Process the merge form formname = "merge_%s_%s_%s" % (tablename, original[table._id], duplicate[table._id]) if form.accepts( post_vars, session, formname=formname, onvalidation=lambda form: self.onvalidation(tablename, form), keepvalues=False, hideerror=False): s3db = current.s3db if form.vars[KEEP.d]: prefix = "%s_" % DUPLICATE original, duplicate = duplicate, original else: prefix = "%s_" % ORIGINAL data = Storage() for key in form.vars: if key.startswith(prefix): fname = key.split("_", 1)[1] data[fname] = form.vars[key] search = False resource = s3db.resource(tablename) try: resource.merge(original[table._id], duplicate[table._id], update=data) except current.auth.permission.error: r.unauthorized() except KeyError: r.error(404, current.ERROR.BAD_RECORD) except: import sys r.error(424, T("Could not merge records. (Internal Error: %s)") % sys.exc_info()[1], next=r.url()) else: # Cleanup bookmark list if mode == "Inclusive": bookmarks[tablename] = [ i for i in record_ids if i not in ids ] if not bookmarks[tablename]: del bookmarks[tablename] search = True elif mode == "Exclusive": bookmarks[tablename].extend(ids) if not selected: search = True # Confirmation message # @todo: Having the link to the merged record in the confirmation # message would be nice, but it's currently not clickable there :/ #result = A(T("Open the merged record"), #_href=r.url(method="read", #id=original[table._id], #vars={})) response.confirmation = T("Records merged successfully.") # Go back to bookmark list if search: self.next = r.url(method="", id=0, vars={}) else: self.next = r.url(id=0, vars={}) # View response.view = self._view(r, "merge.html") return output
def merge(self, r, **attr): """ Merge form for two records @param r: the S3Request @param **attr: the controller attributes for the request @note: this method can always only be POSTed, and requires both "selected" and "mode" in post_vars, as well as the duplicate bookmarks list in session.s3 """ T = current.T session = current.session response = current.response output = dict() tablename = self.tablename # Get the duplicate bookmarks s3 = session.s3 DEDUPLICATE = self.DEDUPLICATE if DEDUPLICATE in s3: bookmarks = s3[DEDUPLICATE] if tablename in bookmarks: record_ids = bookmarks[tablename] # Process the post variables post_vars = r.post_vars mode = post_vars.get("mode") selected = post_vars.get("selected", "") selected = selected.split(",") if mode == "Inclusive": ids = selected elif mode == "Exclusive": ids = [i for i in record_ids if i not in selected] else: # Error ids = [] if len(ids) != 2: r.error(501, T("Please select exactly two records"), next = r.url(id=0, vars={})) # Get the selected records table = self.table query = (table._id == ids[0]) | (table._id == ids[1]) orderby = table.created_on if "created_on" in table else None rows = current.db(query).select(orderby=orderby, limitby=(0, 2)) if len(rows) != 2: r.error(404, current.ERROR.BAD_RECORD, next = r.url(id=0, vars={})) original = rows[0] duplicate = rows[1] # Prepare form construction formfields = [f for f in table if f.readable or f.writable] ORIGINAL, DUPLICATE, KEEP = self.ORIGINAL, self.DUPLICATE, self.KEEP keep_o = KEEP.o in post_vars and post_vars[KEEP.o] keep_d = KEEP.d in post_vars and post_vars[KEEP.d] trs = [] init_requires = self.init_requires index = 1 num_fields = len(formfields) for f in formfields: # Render the widgets oid = "%s_%s" % (ORIGINAL, f.name) did = "%s_%s" % (DUPLICATE, f.name) sid = "swap_%s" % f.name init_requires(f, original[f], duplicate[f]) if keep_o or not any((keep_o, keep_d)): owidget = self.widget(f, original[f], _name=oid, _id=oid, _tabindex=index) else: try: owidget = s3_represent_value(f, value=original[f]) except: owidget = s3_str(original[f]) if keep_d or not any((keep_o, keep_d)): dwidget = self.widget(f, duplicate[f], _name=did, _id=did) else: try: dwidget = s3_represent_value(f, value=duplicate[f]) except: dwidget = s3_str(duplicate[f]) # Swap button if not any((keep_o, keep_d)): swap = INPUT(_value="<-->", _class="swap-button", _id=sid, _type="button", _tabindex = index+num_fields) else: swap = DIV(_class="swap-button") if owidget is None or dwidget is None: continue # Render label row label = f.label trs.append(TR(TD(label, _class="w2p_fl"), TD(), TD(label, _class="w2p_fl"))) # Append widget row trs.append(TR(TD(owidget, _class="mwidget"), TD(swap), TD(dwidget, _class="mwidget"))) index = index + 1 # Show created_on/created_by for each record if "created_on" in table: original_date = original.created_on duplicate_date = duplicate.created_on if "created_by" in table: represent = table.created_by.represent original_author = represent(original.created_by) duplicate_author = represent(duplicate.created_by) created = T("Created on %s by %s") original_created = created % (original_date, original_author) duplicate_created = created % (duplicate_date, duplicate_author) else: created = T("Created on %s") original_created = created % original_date duplicate_created = created % duplicate_date else: original_created = "" duplicate_created = "" # Page title and subtitle output["title"] = T("Merge records") #output["subtitle"] = self.crud_string(tablename, "title_list") # Submit buttons if keep_o or not any((keep_o, keep_d)): submit_original = INPUT(_value=T("Keep Original"), _type="submit", _name=KEEP.o, _id=KEEP.o) else: submit_original = "" if keep_d or not any((keep_o, keep_d)): submit_duplicate = INPUT(_value=T("Keep Duplicate"), _type="submit", _name=KEEP.d, _id=KEEP.d) else: submit_duplicate = "" # Build the form form = FORM(TABLE( THEAD( TR(TH(H3(T("Original"))), TH(), TH(H3(T("Duplicate"))), ), TR(TD(original_created), TD(), TD(duplicate_created), _class="authorinfo", ), ), TBODY(trs), TFOOT( TR(TD(submit_original), TD(), TD(submit_duplicate), ), ), ), # Append mode and selected - required to get back here! hidden = { "mode": "Inclusive", "selected": ",".join(ids), } ) output["form"] = form # Add RESET and CANCEL options output["reset"] = FORM(INPUT(_value=T("Reset"), _type="submit", _name="reset", _id="form-reset"), A(T("Cancel"), _href=r.url(id=0, vars={}), _class="action-lnk"), hidden = {"mode": mode, "selected": ",".join(ids), }, ) # Process the merge form formname = "merge_%s_%s_%s" % (tablename, original[table._id], duplicate[table._id]) if form.accepts(post_vars, session, formname=formname, onvalidation=lambda form: self.onvalidation(tablename, form), keepvalues=False, hideerror=False): s3db = current.s3db if form.vars[KEEP.d]: prefix = "%s_" % DUPLICATE original, duplicate = duplicate, original else: prefix = "%s_" % ORIGINAL data = Storage() for key in form.vars: if key.startswith(prefix): fname = key.split("_", 1)[1] data[fname] = form.vars[key] search = False resource = s3db.resource(tablename) try: resource.merge(original[table._id], duplicate[table._id], update=data) except current.auth.permission.error: r.unauthorized() except KeyError: r.error(404, current.ERROR.BAD_RECORD) except Exception: import sys r.error(424, T("Could not merge records. (Internal Error: %s)") % sys.exc_info()[1], next=r.url()) else: # Cleanup bookmark list if mode == "Inclusive": bookmarks[tablename] = [i for i in record_ids if i not in ids] if not bookmarks[tablename]: del bookmarks[tablename] search = True elif mode == "Exclusive": bookmarks[tablename].extend(ids) if not selected: search = True # Confirmation message # @todo: Having the link to the merged record in the confirmation # message would be nice, but it's currently not clickable there :/ #result = A(T("Open the merged record"), #_href=r.url(method="read", #id=original[table._id], #vars={})) response.confirmation = T("Records merged successfully.") # Go back to bookmark list if search: self.next = r.url(method="", id=0, vars={}) else: self.next = r.url(id=0, vars={}) # View response.view = self._view(r, "merge.html") return output