Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make media items the centre for all moderation activity #4386

Merged
merged 46 commits into from
May 30, 2024
Merged

Conversation

dhruvkb
Copy link
Member

@dhruvkb dhruvkb commented May 25, 2024

Fixes

Fixes #3638 by @sarayourfriend
Fixes #3639 by @sarayourfriend

Description

This PR

  • makes it possible for moderators to view the media model list and change pages, in addition to accessing decision pages and making changes to decision-media relationships
  • adds soft-locking and warning messages so that moderators can avoid potential conflicts
  • makes the media model (and not the media-report model) the root of all moderation activity
  • adds additional information to media model change page
  • provides comprehensive history of pending/reviewed reports and past decisions for any media item
  • adds actions for moderation into the media model change page

This PR also

  • breaks admin templates into smaller, more-manageable components
  • adheres more closely to Django idioms for site links in the admin
  • refactors and organises code in the extremely large model-admin classes

Please note that this PR is intentionally allowed to deviate from some permissions and access controls. These permissions will be tightened in #3640.

Testing Instructions

For the steps involving the Django admin, it is best to use two different profiles (or even browsers) so that you can see the admin from the perspective of a superuser ("deploy") and a moderator ("moderator").

Preparing the data

  1. Take down services (with volumes), bring up the API and initialise.

    just down -v
    just api/up
    just api/init
  2. Get a few image and audio identifiers.

    curl --silent 'http://localhost:50280/v1/images/?page_size=3' | jq '.results[].id'
    curl --silent 'http://localhost:50280/v1/audio/?page_size=3' | jq '.results[].id'
  3. File many different reports of many different reasons against these identifiers. You can use this cURL command as a reference.

    curl \
      -X POST \
      -H 'content-type: application/json' \
      -H 'accept: application/json, */*;q=0.5' \
      -d '{"reason":"<mature|dmca|other>","description":"<any random message of 20+ chars>"}' \
      http://localhost:50280/v1/<image|audio>/<identifier>/report/

Testing

Access control

  1. Open the Django Admin homepage at /admin.
  2. Ensure that relevant models are visible. User "moderator" should see "Images" and "Audios" and some other selected models, whereas user "admin" should see all registered models.
  3. Open a media item's detail page. User "moderator" should see most fields as read-only, whereas user "admin" should see editable fields. Both should be able to make a decision for pending reports (more info below).

Soft-locking

  1. Open a media item's detail page from "moderator"'s session.
  2. On the media list page in "admin"'s session, you should see that media item highlighted in the warning color.
  3. If you visit the same media item's detail page in "admin"'s session, you will also see a warning message at the top stating that "moderator" is viewing this.
  4. If you now refresh the media item's detail page in "moderator"'s session, you will see the same warning but referencing "admin" instead.
  5. The soft-lock endpoint will be polled as long as the detail page is open. Close the detail page and the locks should be cleared in a few seconds.

Additional info

  1. Open the media list view.
  2. You should see the following additional information:
    • column "Has sensitive text?" showing whether the media item has sensitive text
  3. Open a media item's detail page.
  4. You should see the following additional information:
    • links to view the item on the API and Openverse.org in the top right corner
    • the attribution as a read-only field below the media item's fields
    • the license URL as a read-only field below the attribution
    • whether the media item has sensitive text as a read-only field below the license URL
    • a preview of the media item (which in the case of images, can be blurred and un-blurred by clicking the preview)
    • a view of the tags grouped by provider and showing accuracy (if known)
    • information about past decisions
    • information about reports (pending and reviewed)
      • controls to take a new decision for pending reports

Moderation actions

  1. Open a media item's detail page.
  2. Go to the report's list at the bottom of the page.
  3. Select any number of pending reports.
  4. Choose an action and enter moderation notes (optional).
  5. Click on the "Create decision" button. The page will reload (the form submits to the /moderate endpoint which redirects back to /change endpoint).
  6. Verify the results.
    • The new decision should appear under the decision section (with the selected reports shown in the last column).
    • The selected reports should no longer be pending (with the checkbox gone and the decision shown in the last column).

Production deferred

  1. Go to the page to create a new report /admin/api/imagereport/add/. You should see an autocomplete field for "Media obj".
  2. Similarly go to the page to create a new decision /admin/api/audiodecision/add/. You should see an inline tabular admin with autocomplete field for "Media obj".
  3. Now switch your current ENVIRONMENT to "production". These fields should be replaced with raw_id_fields that should offer considerably better performance.

Report admin pages (list, create, change)

  1. The list of reports is shown in a table identical to that present inside the media item change page.
  2. The page to change the report does not allow changing the "Media obj".
  3. The page to create a new report hides away fields that do not make sense before and during filing of reports.

Decision admin pages (list, create, change)

  1. The list of decisions is shown in a table identical to that present inside the media item change page.
  2. The page to change the decision freezes the set of media-decision-through models and does not allow changing them.
  3. The page to create a new decision hides away fields that do not make sense before and during filing of report, and populates the moderator from request.user automatically.

Checklist

  • My pull request has a descriptive title (not a vague title likeUpdate index.md).
  • My pull request targets the default branch of the repository (main) or a parent feature branch.
  • My commit messages follow best practices.
  • My code follows the established code style of the repository.
  • I added or updated tests for the changes I made (if applicable).
  • I added or updated documentation (if applicable).
  • I tried running the project locally and verified that there are no visible errors.
  • I ran the DAG documentation generator (just catalog/generate-docs for catalog
    PRs) or the media properties generator (just catalog/generate-docs media-props
    for the catalog or just api/generate-docs for the API) where applicable.

Developer Certificate of Origin

Developer Certificate of Origin
Developer Certificate of Origin
Version 1.1

Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
1 Letterman Drive
Suite D4700
San Francisco, CA, 94129

Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.


Developer's Certificate of Origin 1.1

By making a contribution to this project, I certify that:

(a) The contribution was created in whole or in part by me and I
    have the right to submit it under the open source license
    indicated in the file; or

(b) The contribution is based upon previous work that, to the best
    of my knowledge, is covered under an appropriate open source
    license and I have the right under that license to submit that
    work with modifications, whether created in whole or in part
    by me, under the same open source license (unless I am
    permitted to submit under a different license), as indicated
    in the file; or

(c) The contribution was provided directly to me by some other
    person who certified (a), (b) or (c) and I have not modified
    it.

(d) I understand and agree that this project and the contribution
    are public and that a record of the contribution (including all
    personal information I submit with it, including my sign-off) is
    maintained indefinitely and may be redistributed consistent with
    this project or the open source license(s) involved.

@dhruvkb dhruvkb added 🟨 priority: medium Not blocking but should be addressed soon 🌟 goal: addition Addition of new feature 💻 aspect: code Concerns the software code in the repository 🐍 tech: python Involves Python 🔧 tech: django Involves Django 🧱 stack: api Related to the Django API labels May 25, 2024
@openverse-bot openverse-bot added the 🕹 aspect: interface Concerns end-users' experience with the software label May 26, 2024
Comment on lines +17 to +25
def handle_redis_exception(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except ConnectionError:
return None

return wrapper
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice idea. I wish we could use something like this in other parts of the app that handle Redis connection failures to reduce all those try/except blocks.

@dhruvkb dhruvkb marked this pull request as ready for review May 27, 2024 23:32
@dhruvkb dhruvkb requested review from a team as code owners May 27, 2024 23:33
@sarayourfriend
Copy link
Collaborator

Here's a one-liner for generating the reports using the commands you shared, Dhruv, for anyone else testing:

curl --silent 'http://localhost:50280/v1/audio/?page_size=3' | jq -r '.results[].id' | xargs -I{} curl \
  -X POST \
  -H 'content-type: application/json' \
  -H 'accept: application/json, */*;q=0.5' \
  -d '{"reason":"mature","description":"blah blah blah"}' \
  http://localhost:50280/v1/audio/\{\}/report/

Replace audio with images for images, etc.

Copy link
Collaborator

@sarayourfriend sarayourfriend left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dhruvkb I think we can get rid of the logic for the media report view and make it just a static view of the media report contents, if that would simplify things. Actioning reports already works from the media view, so this wouldn't affect the ability to moderate reports in prod even though the rest of these moderation workflow features aren't fully finished.

Otherwise, this LGTM, none of my comments are blocking.

api/api/admin/media_report.py Show resolved Hide resolved
api/api/admin/media_report.py Show resolved Hide resolved
api/api/admin/media_report.py Outdated Show resolved Hide resolved
api/api/utils/moderation_lock.py Show resolved Hide resolved
api/api/admin/media_report.py Outdated Show resolved Hide resolved
@sarayourfriend
Copy link
Collaborator

@dhruvkb deferring those two further changes sounds good to me, can you create issues for them so they are tracked?

@sarayourfriend
Copy link
Collaborator

@dhruvkb I caused some nasty merge conflicts for you, sorry! I've got a rebased version of this branch I'll push up in a moment once I have it fully back to the working condition of this branch for you to use and not need to do the rebase yourself.

@sarayourfriend
Copy link
Collaborator

sarayourfriend commented May 29, 2024

The branch media_admin-rebased is this one but rebased on main with tests passing.

@dhruvkb
Copy link
Member Author

dhruvkb commented May 29, 2024

@sarayourfriend thank you very much for resolving the conflicts! I compared the diffs and apart from some reorganisation, the changes lined up. So I updated this branch with your code.

@zackkrida
Copy link
Member

@dhruvkb I'll be reviewing this in the next 24hrs, apologies for the delay! I was so excited by this PR and following its progress that I actually failed to observe that I was one of the assigned reviewers.

@dhruvkb dhruvkb added 🟧 priority: high Stalls work on the project or its dependents and removed 🟨 priority: medium Not blocking but should be addressed soon labels May 30, 2024
Copy link
Collaborator

@AetherUnbound AetherUnbound left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is excellent Dhruv, thanks for your hard work on this! I've tested out a lot of the flows and added a few comments that should be addressed but don't block a merge.

I did try out viewing two media items with different accounts, and then closing one of the tabs. I wasn't able to see the message disappear from the other tab, even after waiting 10 seconds. I did see this error in the console when loading the image change pages:

Uncaught TypeError: document.getElementById(...) is null
    Actions http://localhost:50280/static/admin/js/actions.js:105
    <anonymous> http://localhost:50280/static/admin/js/actions.js:198
actions.js:105:18
    Actions http://localhost:50280/static/admin/js/actions.js:105
    <anonymous> http://localhost:50280/static/admin/js/actions.js:198

Not sure if that's related. Regardless, that can be fixed in a fast-follow IMO (or part of this PR, either way).

Comment on lines +178 to +179
# Start of block lifted from Django source.
from django.urls import path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not have this import hoisted to the top of the file?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only reason is because that is how it was in the Django source file I copied from. I can hoist it, it would not change the behaviour at all.

api/api/admin/media_report.py Show resolved Hide resolved
api/api/admin/media_report.py Outdated Show resolved Hide resolved
api/api/admin/media_report.py Outdated Show resolved Hide resolved
api/api/admin/media_report.py Outdated Show resolved Hide resolved
api/api/models/audio.py Outdated Show resolved Hide resolved
@dhruvkb dhruvkb merged commit 1f24ba1 into main May 30, 2024
47 checks passed
@dhruvkb dhruvkb deleted the media_admin branch May 30, 2024 20:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💻 aspect: code Concerns the software code in the repository 🕹 aspect: interface Concerns end-users' experience with the software 🌟 goal: addition Addition of new feature 🟧 priority: high Stalls work on the project or its dependents 🧱 stack: api Related to the Django API 🔧 tech: django Involves Django 🐍 tech: python Involves Python
Projects
Archived in project
5 participants