Skip to content

Commit bba018d

Browse files
authored
Merge pull request #5140 from dojutsu-user/admin-action-for-version
Add admin functions for wiping a version
2 parents cca41b7 + b206c70 commit bba018d

File tree

6 files changed

+168
-10
lines changed

6 files changed

+168
-10
lines changed

readthedocs/builds/admin.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from readthedocs.builds.models import Build, BuildCommandResult, Version
99
from readthedocs.core.utils import trigger_build
10+
from readthedocs.core.utils.general import wipe_version_via_slugs
1011

1112

1213
class BuildCommandResultInline(admin.TabularInline):
@@ -57,7 +58,22 @@ class VersionAdmin(GuardedModelAdmin):
5758
list_filter = ('type', 'privacy_level', 'active', 'built')
5859
search_fields = ('slug', 'project__slug')
5960
raw_id_fields = ('project',)
60-
actions = ['build_version']
61+
actions = ['wipe_selected_versions', 'build_version']
62+
63+
def wipe_selected_versions(self, request, queryset):
64+
"""Wipes the selected versions."""
65+
for version in queryset:
66+
wipe_version_via_slugs(
67+
version_slug=version.slug,
68+
project_slug=version.project.slug
69+
)
70+
self.message_user(
71+
request,
72+
'Wiped {}.'.format(version.slug),
73+
level=messages.SUCCESS
74+
)
75+
76+
wipe_selected_versions.short_description = 'Wipe selected versions'
6177

6278
def build_version(self, request, queryset):
6379
"""Trigger a build for the project version."""

readthedocs/core/utils/general.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os
4+
5+
from django.shortcuts import get_object_or_404
6+
7+
from readthedocs.core.utils import broadcast
8+
from readthedocs.projects.tasks import remove_dirs
9+
from readthedocs.builds.models import Version
10+
11+
12+
def wipe_version_via_slugs(version_slug, project_slug):
13+
"""Wipes the given version of a given project."""
14+
version = get_object_or_404(
15+
Version,
16+
slug=version_slug,
17+
project__slug=project_slug,
18+
)
19+
del_dirs = [
20+
os.path.join(version.project.doc_path, 'checkouts', version.slug),
21+
os.path.join(version.project.doc_path, 'envs', version.slug),
22+
os.path.join(version.project.doc_path, 'conda', version.slug),
23+
]
24+
for del_dir in del_dirs:
25+
broadcast(type='build', task=remove_dirs, args=[(del_dir,)])

readthedocs/core/views/__init__.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
documentation and header rendering, and server errors.
77
"""
88

9-
from __future__ import absolute_import
10-
from __future__ import division
119
import os
1210
import logging
1311
from urllib.parse import urlparse
@@ -19,6 +17,7 @@
1917

2018

2119
from readthedocs.builds.models import Version
20+
from readthedocs.core.utils.general import wipe_version_via_slugs
2221
from readthedocs.core.resolver import resolve_path
2322
from readthedocs.core.symlink import PrivateSymlink, PublicSymlink
2423
from readthedocs.core.utils import broadcast
@@ -89,13 +88,10 @@ def wipe_version(request, project_slug, version_slug):
8988
raise Http404('You must own this project to wipe it.')
9089

9190
if request.method == 'POST':
92-
del_dirs = [
93-
os.path.join(version.project.doc_path, 'checkouts', version.slug),
94-
os.path.join(version.project.doc_path, 'envs', version.slug),
95-
os.path.join(version.project.doc_path, 'conda', version.slug),
96-
]
97-
for del_dir in del_dirs:
98-
broadcast(type='build', task=remove_dirs, args=[(del_dir,)])
91+
wipe_version_via_slugs(
92+
version_slug=version_slug,
93+
project_slug=project_slug
94+
)
9995
return redirect('project_version_list', project_slug)
10096
return render(
10197
request,

readthedocs/rtd_tests/tests/test_core_utils.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
# -*- coding: utf-8 -*-
22
"""Test core util functions."""
33

4+
import os
45
import mock
6+
7+
from mock import call
8+
from django.http import Http404
59
from django.test import TestCase
610
from django_dynamic_fixture import get
711

812
from readthedocs.builds.models import Version
13+
from readthedocs.core.utils.general import wipe_version_via_slugs
14+
from readthedocs.projects.tasks import remove_dirs
915
from readthedocs.core.utils import slugify, trigger_build
1016
from readthedocs.projects.models import Project
1117

@@ -153,3 +159,58 @@ def test_slugify(self):
153159
slugify('A title_-_with separated parts', dns_safe=False),
154160
'a-title_-_with-separated-parts',
155161
)
162+
163+
@mock.patch('readthedocs.core.utils.general.broadcast')
164+
def test_wipe_version_via_slug(self, mock_broadcast):
165+
wipe_version_via_slugs(
166+
version_slug=self.version.slug,
167+
project_slug=self.version.project.slug
168+
)
169+
expected_del_dirs = [
170+
os.path.join(self.version.project.doc_path, 'checkouts', self.version.slug),
171+
os.path.join(self.version.project.doc_path, 'envs', self.version.slug),
172+
os.path.join(self.version.project.doc_path, 'conda', self.version.slug),
173+
]
174+
175+
mock_broadcast.assert_has_calls(
176+
[
177+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[0],)]),
178+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[1],)]),
179+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[2],)]),
180+
],
181+
any_order=False
182+
)
183+
184+
@mock.patch('readthedocs.core.utils.general.broadcast')
185+
def test_wipe_version_via_slug_wrong_param(self, mock_broadcast):
186+
self.assertFalse(Version.objects.filter(slug='wrong-slug').exists())
187+
with self.assertRaises(Http404):
188+
wipe_version_via_slugs(
189+
version_slug='wrong-slug',
190+
project_slug=self.version.project.slug
191+
)
192+
mock_broadcast.assert_not_called()
193+
194+
@mock.patch('readthedocs.core.utils.general.broadcast')
195+
def test_wipe_version_via_slugs_same_version_slug_with_diff_proj(self, mock_broadcast):
196+
project_2 = get(Project)
197+
version_2 = get(Version, project=project_2, slug=self.version.slug)
198+
wipe_version_via_slugs(
199+
version_slug=version_2.slug,
200+
project_slug=project_2.slug,
201+
)
202+
203+
expected_del_dirs = [
204+
os.path.join(version_2.project.doc_path, 'checkouts', version_2.slug),
205+
os.path.join(version_2.project.doc_path, 'envs', version_2.slug),
206+
os.path.join(version_2.project.doc_path, 'conda', version_2.slug),
207+
]
208+
209+
mock_broadcast.assert_has_calls(
210+
[
211+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[0],)]),
212+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[1],)]),
213+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[2],)]),
214+
],
215+
any_order=False
216+
)

readthedocs/rtd_tests/tests/versions/__init__.py

Whitespace-only changes.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import os
4+
import mock
5+
6+
from mock import call
7+
import django_dynamic_fixture as fixture
8+
from django.test import TestCase
9+
from django.contrib.admin.helpers import ACTION_CHECKBOX_NAME
10+
from django.contrib.auth.models import User
11+
from django import urls
12+
13+
from readthedocs.builds.models import Version
14+
from readthedocs.core.models import UserProfile
15+
from readthedocs.projects.models import Project
16+
from readthedocs.projects.tasks import remove_dirs
17+
18+
19+
class VersionAdminActionsTest(TestCase):
20+
21+
@classmethod
22+
def setUpTestData(cls):
23+
cls.owner = fixture.get(User)
24+
cls.profile = fixture.get(UserProfile, user=cls.owner, banned=False)
25+
cls.admin = fixture.get(User, is_staff=True, is_superuser=True)
26+
cls.project = fixture.get(
27+
Project,
28+
main_language_project=None,
29+
users=[cls.owner],
30+
)
31+
cls.version = fixture.get(Version, project=cls.project)
32+
33+
def setUp(self):
34+
self.client.force_login(self.admin)
35+
36+
@mock.patch('readthedocs.core.utils.general.broadcast')
37+
def test_wipe_selected_version(self, mock_broadcast):
38+
action_data = {
39+
ACTION_CHECKBOX_NAME: [self.version.pk],
40+
'action': 'wipe_selected_versions',
41+
'post': 'yes',
42+
}
43+
resp = self.client.post(
44+
urls.reverse('admin:builds_version_changelist'),
45+
action_data
46+
)
47+
expected_del_dirs = [
48+
os.path.join(self.version.project.doc_path, 'checkouts', self.version.slug),
49+
os.path.join(self.version.project.doc_path, 'envs', self.version.slug),
50+
os.path.join(self.version.project.doc_path, 'conda', self.version.slug),
51+
]
52+
53+
mock_broadcast.assert_has_calls(
54+
[
55+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[0],)]),
56+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[1],)]),
57+
call(type='build', task=remove_dirs, args=[(expected_del_dirs[2],)]),
58+
],
59+
any_order=False
60+
)

0 commit comments

Comments
 (0)