Skip to content

Commit 84d6f44

Browse files
authored
Merge pull request #165 from joalla/foldermove
Move release between folders / Remove from all folders
2 parents 98059c0 + f0d3af2 commit 84d6f44

5 files changed

Lines changed: 90 additions & 13 deletions

File tree

.github/workflows/discogs_client-build.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-20.04
1212
strategy:
1313
matrix:
14-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
14+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
1515
steps:
1616
- uses: actions/checkout@v4
1717
- name: Set up Python ${{ matrix.python-version }}

discogs_client/models.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,7 @@ class CollectionFolder(PrimaryAPIObject):
745745
count = SimpleField() #:
746746

747747
def __init__(self, client, dict_):
748-
super(CollectionFolder, self).__init__(client, dict_)
748+
super().__init__(client, dict_)
749749

750750
@property
751751
def releases(self):
@@ -754,14 +754,34 @@ def releases(self):
754754

755755
def add_release(self, release):
756756
release_id = release.id if isinstance(release, Release) else release
757-
add_release_url = self.fetch('resource_url') + '/releases/{}'.format(release_id)
758-
self.client._post(add_release_url, None)
757+
resource_url = self.fetch('resource_url')
758+
self.client._post(f"{resource_url}/releases/{release_id}", None)
759759

760760
def remove_release(self, instance):
761+
"""Remove a collection item entirely.
762+
"""
761763
if not isinstance(instance, CollectionItemInstance):
762764
raise TypeError('instance must be of type CollectionItemInstance')
763-
instance_url = self.fetch('resource_url') + '/releases/{0}/instances/{1}'.format(instance.id, instance.instance_id)
764-
self.client._delete(instance_url)
765+
resource_url = self.fetch('resource_url')
766+
self.client._delete(f"{resource_url}/releases/{instance.id}/instances/{instance.instance_id}")
767+
768+
def move_release(self, instance, target_folder_id):
769+
"""Move a collection item to another folder.
770+
771+
Moving to folder id 1 moves to the "Uncategorized" folder.
772+
"""
773+
if not isinstance(instance, CollectionItemInstance):
774+
raise TypeError('instance must be of type CollectionItemInstance')
775+
resource_url = self.fetch('resource_url')
776+
self.client._post(
777+
f"{resource_url}/releases/{instance.id}/instances/{instance.instance_id}",
778+
{"folder_id": target_folder_id},
779+
)
780+
781+
def uncategorize_release(self, instance):
782+
"""Move a collection item to the "Uncategorized" folder.
783+
"""
784+
self.move_release(instance, 1)
765785

766786
def __repr__(self):
767787
return '<CollectionFolder {0!r} {1!r}>'.format(self.id, self.name)

discogs_client/tests/test_models.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import unittest
2-
from discogs_client.models import Artist, Release, ListItem, CollectionValue
2+
from discogs_client.models import Artist, Release, ListItem, CollectionValue, CollectionItemInstance
33
from discogs_client.tests import DiscogsClientTestCase
44
from discogs_client.exceptions import HTTPError
55

@@ -265,7 +265,6 @@ def test_listing(self):
265265
self.assertEqual(method, 'GET')
266266
self.assertEqual(url, '/marketplace/listings/150899904')
267267

268-
269268
def test_collection(self):
270269
"""Collection folders can be manipulated"""
271270
# Fetch the users collection folders from the filesystem
@@ -314,6 +313,41 @@ def test_collection(self):
314313
self.assertEqual(method, 'POST')
315314
self.assertEqual(url, '/users/example/collection/folders/1/releases/1')
316315

316+
def test_collection_move_release(self):
317+
"""Collection items can be moved to another folder"""
318+
# Fetch the users collection folders from the filesystem
319+
u = self.d.user("example")
320+
self.assertEqual(u.collection_folders[2].id, 2)
321+
322+
# Mock expected responses for move_release test using "MemoryFetcher"
323+
self.m._fetcher.fetcher.responses = {
324+
"/users/example/collection/folders": (b'''
325+
{"folders": [
326+
{"resource_url": "/users/example/collection/folders/0", "id": 0, "name": "All"},
327+
{"resource_url": "/users/example/collection/folders/1", "id": 1, "name": "Uncategorized folder"},
328+
{"resource_url": "/users/example/collection/folders/2", "id": 2, "name": "Collection folder 2"}
329+
]}
330+
''', 200),
331+
# Mock the response of the POST request to the instance resource URL
332+
"/users/example/collection/folders/1/releases/123456/instances/123": (b"", 204),
333+
}
334+
335+
# Bind the user to the MemoryFetcher
336+
u.client = self.m
337+
338+
# Mock a collection item instance
339+
instance = CollectionItemInstance(
340+
client=u.client,
341+
dict_={"id": 123456, "instance_id": 123}
342+
)
343+
# Perform the move
344+
u.collection_folders[1].move_release(instance, 2)
345+
346+
# Verify
347+
method, url, _, _ = self.m._fetcher.last_request
348+
self.assertEqual(method, "POST")
349+
self.assertEqual(url, "/users/example/collection/folders/1/releases/123456/instances/123")
350+
317351
def test_delete_object(self):
318352
"""Can request DELETE on an APIObject"""
319353
u = self.d.user('example')

docs/source/conf.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@
8080
# MyST extenstion configuration
8181
myst_heading_anchors = 7
8282
myst_enable_extensions = [
83-
"substitution"
83+
"substitution",
84+
"colon_fence",
8485
]
8586
myst_substitutions = {
8687
"class": "I'm a **substitution**"

docs/source/fetching_data.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ me.collection_folders[2].add_release(17392219)
231231
_{meth}`.add_release` also accepts {class}`.Release` objects_
232232

233233

234-
### Removing a Release from a Collection Folder
234+
### Removing a Release from the Collection
235235

236236
Removing a single release instance identified by its index:
237237

@@ -242,6 +242,10 @@ releases = folder.releases
242242
folder.remove_release(releases[0])
243243
```
244244

245+
:::{caution}
246+
The {meth}`.remove_release` method deletes from the collection entirely. To remove an instance from a folder only, use the {meth}`.uncategorize_release` method.
247+
:::
248+
245249
To filter out which instance to remove we could also use the attributes of the {class}`.Release` object attached to the {class}`.CollectionItemInstance`:
246250

247251
```python
@@ -251,16 +255,34 @@ for instance in folder.releases:
251255
folder.remove_release(instance)
252256
```
253257

254-
Another approach to removing instances from collection folders if we know a release ID already would be to make use of the {meth}`.collection_items` method. This way we could delete all the instances from all its containing folders:
258+
### Removing a Release from a Folder
259+
260+
To remove a release from a collection folder, we need to know the release ID already and make use of the {meth}`.collection_items` method. This way we could remove all the instances from all its containing folders (uncategorize them):
255261

256262
```python
257263
release_instances = me.collection_items(22155985)
258264
for instance in release_instances:
259265
folder = me.collection_folders[instance.folder_id]
260-
folder.remove_release(instance)
266+
folder.uncategorize_release(instance)
267+
```
268+
269+
### Moving a Release to a Different Folder
270+
271+
To move a release from a collection folder to another one, again we need to know the release ID already and make use of the {meth}`.collection_items` method. We also need to know the ID of the target folder we want to move to (use {attr}`.collection_folders`). We move all the instances from all its containing folders to a specified target folder:
272+
273+
```python
274+
release_instances = me.collection_items(22155985)
275+
target_folder = 1
276+
for folder in me.collection_folders:
277+
if folder.name == "My Target Folder":
278+
target_folder = folder.id
279+
280+
for instance in release_instances:
281+
folder = me.collection_folders[instance.folder_id]
282+
folder.move_release(instance, target_folder)
261283
```
262284

263-
_{meth}`.remove_release` only accepts {class}`.CollectionItemInstance` objects_
285+
_{meth}`.remove_release`, {meth}`.move_release` and {meth}`.uncategorize_release` only accept {class}`.CollectionItemInstance` objects_
264286

265287

266288
## Using {meth}`~discogs_client.models.PrimaryAPIObject.fetch` to get other data

0 commit comments

Comments
 (0)