Repository: internetarchive/openlibrary

Issue:
Issues Blocking & Reverting Patrons whose history includes /type/delete
<!-- What problem are we solving? What does the experience look like today? What are the symptoms? -->

Related to #5753

Trying to revert patron edits which include `/type/delete` can cause errors. To repro **as an admin**, one needs to use the browser developer tools / inspector to add `?debug=true` to the `<form>` on the admin page for the `block and revert changes` button.

### Evidence / Screenshot (if possible)

![image](https://user-images.githubusercontent.com/978325/179794562-a231e76d-c847-48e5-b331-5a8d5c39cf7f.png)


### Relevant url?
<!-- `https://openlibrary.org/...` -->

### Steps to Reproduce
<!-- What steps caused you to find the bug? -->
1. Go to ...
2. Do ...

<!-- What actually happened after these steps? What did you expect to happen? -->
* Actual:
* Expected:

### Details

- **Logged in (Y/N)?**
- **Browser type/version?**
- **Operating system?**
- **Environment (prod/dev/local)?** prod
<!-- If not sure, put prod -->

### Proposal & Constraints
<!-- What is the proposed solution / implementation? Is there a precedent of this approach succeeding elsewhere? -->

### Related files
<!-- Files related to this issue; this is super useful for new contributors who might want to help! If you're not sure, leave this blank; a maintainer will add them. -->

### Stakeholders
<!-- @ tag stakeholders of this bug -->



Discussion:
@jimchamp  See:

```
diff --git a/openlibrary/plugins/admin/code.py b/openlibrary/plugins/admin/code.py
index 0c3a64cda..0ce564db8 100644
--- a/openlibrary/plugins/admin/code.py
+++ b/openlibrary/plugins/admin/code.py
@@ -297,10 +297,18 @@ class people_view:
 
     def POST_block_account_and_revert(self, account):
         account.block()
-        changes = account.get_recentchanges(limit=1000)
-        changeset_ids = [c.id for c in changes]
-        ipaddress_view().revert(changeset_ids, "Reverted Spam")
-        add_flash_message("info", "Blocked the account and reverted all edits.")
+        i = 0
+        edits = 0
+        stop = False
+        while not stop:
+            changes = account.get_recentchanges(limit=100, offset=100*i)
+            changeset_ids = [c.id for c in changes]
+            _, len_docs = ipaddress_view().revert(changeset_ids, "Reverted Spam")
+            edits += len_docs
+            i += 1
+            if len(changes) < 100:
+                stop = True
+        add_flash_message("info", f"Blocked the account and reverted all {edits} edits.")
         raise web.seeother(web.ctx.path)
 
     def POST_unblock_account(self, account):
@@ -431,10 +439,11 @@ class ipaddress_view:
             for cid in changeset_ids
             for c in site.get_change(cid).changes
         ]
-
+        docs = [doc for doc in docs if doc.get('type', {}).get('key') is not '/type/delete']
         logger.debug("Reverting %d docs", len(docs))
         data = {"reverted_changesets": [str(cid) for cid in changeset_ids]}
-        return web.ctx.site.save_many(docs, action="revert", data=data, comment=comment)
+        manifest = web.ctx.site.save_many(docs, action="revert", data=data, comment=comment)
+        return manifest, len(docs)
 
 
 class stats:
diff --git a/openlibrary/templates/admin/people/view.html b/openlibrary/templates/admin/people/view.html
index 96da86d8d..2b4efba58 100644
--- a/openlibrary/templates/admin/people/view.html
+++ b/openlibrary/templates/admin/people/view.html
@@ -106,7 +106,7 @@ $ has_profile = person.get_user() is not None
 <div id="contentBody">
 
     <div class="status status-$person.status">
-        <form method="POST">
+        <form method="POST" action="?debug=true">
             Status: <span class="status">$person.status</span>
 
         $if person.status in ['active', 'verified']:
```

There were 2 problem:
1. edits of /type/delete were breaking things
2. infobase was breaking when save_many request with changeset of 1000 items submitted.

This code ignores /type/delete records and then performs save_many of batches of 100 at a time.

This also fixes a 3rd problem which is, before, we were just fetching 1000 items irrespective of whether the patron had made more than 1000 edits! We basically weren't reverting everything, doh!

This code is on ol-dev1 ol-mek

Resolving patch:
diff --git a/openlibrary/plugins/admin/code.py b/openlibrary/plugins/admin/code.py
--- a/openlibrary/plugins/admin/code.py
+++ b/openlibrary/plugins/admin/code.py
@@ -297,10 +297,20 @@ def POST_block_account(self, account):
 
     def POST_block_account_and_revert(self, account):
         account.block()
-        changes = account.get_recentchanges(limit=1000)
-        changeset_ids = [c.id for c in changes]
-        ipaddress_view().revert(changeset_ids, "Reverted Spam")
-        add_flash_message("info", "Blocked the account and reverted all edits.")
+        i = 0
+        edits = 0
+        stop = False
+        while not stop:
+            changes = account.get_recentchanges(limit=100, offset=100 * i)
+            changeset_ids = [c.id for c in changes]
+            _, len_docs = ipaddress_view().revert(changeset_ids, "Reverted Spam")
+            edits += len_docs
+            i += 1
+            if len(changes) < 100:
+                stop = True
+        add_flash_message(
+            "info", f"Blocked the account and reverted all {edits} edits."
+        )
         raise web.seeother(web.ctx.path)
 
     def POST_unblock_account(self, account):
@@ -431,10 +441,13 @@ def revert(self, changeset_ids, comment):
             for cid in changeset_ids
             for c in site.get_change(cid).changes
         ]
-
+        docs = [doc for doc in docs if doc.get('type', {}).get('key') != '/type/delete']
         logger.debug("Reverting %d docs", len(docs))
         data = {"reverted_changesets": [str(cid) for cid in changeset_ids]}
-        return web.ctx.site.save_many(docs, action="revert", data=data, comment=comment)
+        manifest = web.ctx.site.save_many(
+            docs, action="revert", data=data, comment=comment
+        )
+        return manifest, len(docs)
 
 
 class stats:


Test patch: