mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-23 08:36:32 +00:00
Merge pull request #2327 from bookwyrm-social/imports-admin
Imports admin
This commit is contained in:
commit
91b935bc0a
8 changed files with 185 additions and 2 deletions
|
@ -48,6 +48,16 @@ class ImportJob(models.Model):
|
|||
"""items that haven't been processed yet"""
|
||||
return self.items.filter(fail_reason__isnull=True, book__isnull=True)
|
||||
|
||||
@property
|
||||
def item_count(self):
|
||||
"""How many books do you want to import???"""
|
||||
return self.items.count()
|
||||
|
||||
@property
|
||||
def pending_item_count(self):
|
||||
"""And how many pending items??"""
|
||||
return self.pending_items.count()
|
||||
|
||||
|
||||
class ImportItem(models.Model):
|
||||
"""a single line of a csv being imported"""
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
{% extends 'components/modal.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block modal-title %}{% trans "Mark import as complete?" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
{% trans "This action cannot be un-done" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<form name="complete-import-{{ import.id }}" action="{% url 'settings-imports-complete' import.id %}" method="POST" class="is-flex-grow-1">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="id" value="{{ list.id }}">
|
||||
<div class="buttons is-right is-flex-grow-1">
|
||||
<button type="button" class="button" data-modal-close>
|
||||
{% trans "Cancel" %}
|
||||
</button>
|
||||
<button class="button is-danger" type="submit">
|
||||
{% trans "Confirm" %}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
86
bookwyrm/templates/settings/imports/imports.html
Normal file
86
bookwyrm/templates/settings/imports/imports.html
Normal file
|
@ -0,0 +1,86 @@
|
|||
{% extends 'settings/layout.html' %}
|
||||
{% load i18n %}
|
||||
{% load utilities %}
|
||||
|
||||
{% block title %}{% trans "Imports" %}{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
{% trans "Imports" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block panel %}
|
||||
|
||||
<div class="block">
|
||||
<div class="tabs">
|
||||
<ul>
|
||||
{% url 'settings-imports' as url %}
|
||||
<li {% if request.path in url %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Active" %}</a>
|
||||
</li>
|
||||
{% url 'settings-imports' status="complete" as url %}
|
||||
<li {% if url in request.path %}class="is-active" aria-current="page"{% endif %}>
|
||||
<a href="{{ url }}">{% trans "Completed" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container block content">
|
||||
{% if status == "active" %}
|
||||
<div class="notification is-warning">
|
||||
<p>{% trans "Marking an import as complete will <em>not</em> stop it." %}</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
<table class="table is-striped is-fullwidth">
|
||||
<tr>
|
||||
{% url 'settings-imports' as url %}
|
||||
<th>
|
||||
{% trans "ID" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "User" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Date Created" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Items" %}
|
||||
</th>
|
||||
<th>
|
||||
{% trans "Pending items" %}
|
||||
</th>
|
||||
{% if status == "active" %}
|
||||
<th>{% trans "Actions" %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% for import in imports %}
|
||||
<tr>
|
||||
<td>{{ import.id }}</td>
|
||||
<td class="overflow-wrap-anywhere">
|
||||
<a href="{% url 'settings-user' user.id %}">{{ import.user|username }}</a>
|
||||
</td>
|
||||
<td>{{ import.created_date }}</td>
|
||||
<td>{{ import.item_count }}</td>
|
||||
<td>{{ import.pending_item_count }}</td>
|
||||
{% if status == "active" %}
|
||||
<td>
|
||||
{% join "complete" import.id as modal_id %}
|
||||
<button type="button" data-modal-open="{{ modal_id }}" class="button is-danger">{% trans "Mark as complete" %}</button>
|
||||
{% include "settings/imports/complete_import_modal.html" with id=modal_id %}
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% if not imports %}
|
||||
<tr>
|
||||
<td colspan="6">
|
||||
<em>{% trans "No matching imports founds." %} </em>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% include 'snippets/pagination.html' with page=users path=request.path %}
|
||||
{% endblock %}
|
||||
|
|
@ -76,6 +76,12 @@
|
|||
{% endif %}
|
||||
{% if perms.edit_instance_settings %}
|
||||
<h2 class="menu-label">{% trans "System" %}</h2>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
{% url 'settings-imports' as url %}
|
||||
<a href="{{ url }}"{% if url in request.path %} class="is-active" aria-selected="true"{% endif %}>{% trans "Imports" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="menu-list">
|
||||
<li>
|
||||
{% url 'settings-celery' as url %}
|
||||
|
|
|
@ -291,6 +291,16 @@ urlpatterns = [
|
|||
views.Report.as_view(),
|
||||
name="report-link",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/imports/(?P<status>(complete|active))?/?$",
|
||||
views.ImportList.as_view(),
|
||||
name="settings-imports",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/imports/(?P<import_id>\d+)/complete?$",
|
||||
views.ImportList.as_view(),
|
||||
name="settings-imports-complete",
|
||||
),
|
||||
re_path(
|
||||
r"^settings/celery/?$", views.CeleryStatus.as_view(), name="settings-celery"
|
||||
),
|
||||
|
|
|
@ -10,6 +10,7 @@ from .admin.federation import Federation, FederatedServer
|
|||
from .admin.federation import AddFederatedServer, ImportServerBlocklist
|
||||
from .admin.federation import block_server, unblock_server, refresh_server
|
||||
from .admin.email_blocklist import EmailBlocklist
|
||||
from .admin.imports import ImportList
|
||||
from .admin.ip_blocklist import IPBlocklist
|
||||
from .admin.invite import ManageInvites, Invite, InviteRequest
|
||||
from .admin.invite import ManageInviteRequests, ignore_invite_request
|
||||
|
|
43
bookwyrm/views/admin/imports.py
Normal file
43
bookwyrm/views/admin/imports.py
Normal file
|
@ -0,0 +1,43 @@
|
|||
""" manage imports """
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.core.paginator import Paginator
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.settings import PAGE_LENGTH
|
||||
|
||||
|
||||
# pylint: disable=no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@method_decorator(
|
||||
permission_required("bookwyrm.moderate_user", raise_exception=True),
|
||||
name="dispatch",
|
||||
)
|
||||
class ImportList(View):
|
||||
"""admin view of imports on this server"""
|
||||
|
||||
def get(self, request, status="active"):
|
||||
"""list of imports"""
|
||||
complete = status == "complete"
|
||||
imports = models.ImportJob.objects.filter(complete=complete)
|
||||
paginated = Paginator(imports, PAGE_LENGTH)
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data = {
|
||||
"imports": page,
|
||||
"page_range": paginated.get_elided_page_range(
|
||||
page.number, on_each_side=2, on_ends=1
|
||||
),
|
||||
"status": status,
|
||||
}
|
||||
return TemplateResponse(request, "settings/imports/imports.html", data)
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def post(self, request, import_id):
|
||||
"""Mark an import as complete"""
|
||||
import_job = get_object_or_404(models.ImportJob, id=import_id)
|
||||
import_job.complete = True
|
||||
import_job.save()
|
||||
return redirect("settings-imports")
|
|
@ -10,7 +10,7 @@ from bookwyrm import forms, models
|
|||
from bookwyrm.settings import PAGE_LENGTH
|
||||
|
||||
|
||||
# pylint: disable= no-self-use
|
||||
# pylint: disable=no-self-use
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@method_decorator(
|
||||
permission_required("bookwyrm.moderate_user", raise_exception=True),
|
||||
|
@ -55,8 +55,12 @@ class UserAdminList(View):
|
|||
users = users.order_by(sort)
|
||||
|
||||
paginated = Paginator(users, PAGE_LENGTH)
|
||||
page = paginated.get_page(request.GET.get("page"))
|
||||
data = {
|
||||
"users": paginated.get_page(request.GET.get("page")),
|
||||
"users": page,
|
||||
"page_range": paginated.get_elided_page_range(
|
||||
page.number, on_each_side=2, on_ends=1
|
||||
),
|
||||
"sort": sort,
|
||||
"server": server,
|
||||
"status": status,
|
||||
|
|
Loading…
Reference in a new issue