Multi-platform: support multiple files per version #201

Merged
Oleg-Komarov merged 43 commits from multi-os into main 2024-07-09 16:27:46 +02:00
5 changed files with 51 additions and 51 deletions
Showing only changes of commit a3bcd42114 - Show all commits

View File

@ -1,4 +1,5 @@
from typing import List from typing import List
from urllib.parse import urlencode
import logging import logging
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@ -754,14 +755,55 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
permissions.append({'slug': slug, 'reason': reason, 'name': all_permission_names[slug]}) permissions.append({'slug': slug, 'reason': reason, 'name': all_permission_names[slug]})
return permissions return permissions
def _get_download_name(self) -> str:
"""Return a file name for downloads."""
replace_char = f'{self}'.replace('.', '-')
Oleg-Komarov marked this conversation as resolved Outdated

The filename below
filename = f'{self.extension.type_slug_singular}-{self.extension.slug}-v{self.version}.zip'

looks like a better name: doesn't rely on self.__str__.

It also looks like these file names should be the same in both places.

The `filename` below `filename = f'{self.extension.type_slug_singular}-{self.extension.slug}-v{self.version}.zip'` looks like a better name: doesn't rely on `self.__str__`. It also looks like these file names should be the same in both places.
return f'{utils.slugify(replace_char)}.zip'
Oleg-Komarov marked this conversation as resolved Outdated

to avoid making platform into file ext, maybe -?

to avoid making platform into file ext, maybe `-`?
def get_download_url(self, platform=None, append_repository_and_compatibility=True) -> str:
filename = f'{self.extension.type_slug_singular}-{self.extension.slug}-v{self.version}.zip'
if platform:
download_url = reverse(
'extensions:version-platform-download',
kwargs={
'type_slug': self.extension.type_slug,
'slug': self.extension.slug,
'version': self.version,
'platform': platform,
'filename': filename,
},
)
else:
download_url = reverse(
'extensions:version-download',
kwargs={
'type_slug': self.extension.type_slug,
'slug': self.extension.slug,
'version': self.version,
'filename': filename,
},
)
if append_repository_and_compatibility:
params = {
'repository': '/api/v1/extensions/',
'blender_version_min': self.blender_version_min,
}
if self.blender_version_max:
params['blender_version_max'] = self.blender_version_max
if platforms := self.platforms.all():
params['platforms'] = ','.join([p.slug for p in platforms])
query_string = urlencode(params)
download_url += f'?{query_string}'
return download_url
def get_download_list(self) -> List[dict]: def get_download_list(self) -> List[dict]:
files = list(self.files.all()) files = list(self.files.all())
Oleg-Komarov marked this conversation as resolved Outdated

Would it make sense to use file hash in the URL?
This would allow skipping the file-selecting logic in the public view altogether by using the hash to retrieve the file directly.

Would it make sense to use file hash in the URL? This would allow skipping the file-selecting logic in the `public` view altogether by using the hash to retrieve the file directly.
if len(files) == 1: if len(files) == 1:
file = files[0] file = files[0]
return [ return [
{ {
'name': file.download_name(), 'name': self._get_download_name(),
'url': file.download_url(platform=None), 'url': self.get_download_url(platform=None),
'size': file.size_bytes, 'size': file.size_bytes,
} }
] ]
@ -778,8 +820,8 @@ class Version(CreatedModifiedMixin, TrackChangesMixin, models.Model):
return [ return [
{ {
'platform': p, 'platform': p,
'name': file.download_name(), 'name': self._get_download_name(),
'url': file.download_url(platform=p), 'url': self.get_download_url(platform=p),
'size': file.size_bytes, 'size': file.size_bytes,
} }
for p, file in platform2file.items() for p, file in platform2file.items()

View File

@ -92,7 +92,7 @@ class ListedExtensionsSerializer(serializers.ModelSerializer):
'archive_hash': matching_file.original_hash, 'archive_hash': matching_file.original_hash,
'archive_size': matching_file.size_bytes, 'archive_size': matching_file.size_bytes,
'archive_url': self.request.build_absolute_uri( 'archive_url': self.request.build_absolute_uri(
matching_file.download_url( matching_version.download_url(
platform=self.platform, platform=self.platform,
append_repository_and_compatibility=False, append_repository_and_compatibility=False,
) )

View File

@ -63,9 +63,12 @@ def extension_version_download(request, type_slug, slug, version, filename):
def extension_version_platform_download(request, type_slug, slug, version, platform, filename): def extension_version_platform_download(request, type_slug, slug, version, platform, filename):
"""Download an extension version and count downloads. """Download an extension version and count downloads.
This method processes urls constructed by Version.get_download_list.
The `filename` parameter is used to pass a file name ending with `.zip`. The `filename` parameter is used to pass a file name ending with `.zip`.
This is a convention Blender uses to initiate an extension installation on an HTML anchor This is a convention Blender uses to initiate an extension installation on an HTML anchor
drag&drop. drag&drop.
Also see $arg_filename usage in playbooks/templates/nginx/http.conf
""" """
extension_version = get_object_or_404(Version, extension__slug=slug, version=version) extension_version = get_object_or_404(Version, extension__slug=slug, version=version)
ExtensionDownload.create_from_request(request, object_id=extension_version.extension_id) ExtensionDownload.create_from_request(request, object_id=extension_version.extension_id)

View File

@ -1,11 +1,9 @@
from pathlib import Path from pathlib import Path
from typing import Dict, Any from typing import Dict, Any
from urllib.parse import urlencode
import logging import logging
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.db import models from django.db import models
from django.urls import reverse
from common.model_mixins import CreatedModifiedMixin, TrackChangesMixin from common.model_mixins import CreatedModifiedMixin, TrackChangesMixin
from files.utils import get_sha256, guess_mimetype_from_ext, get_thumbnail_upload_to from files.utils import get_sha256, guess_mimetype_from_ext, get_thumbnail_upload_to
@ -204,49 +202,6 @@ class File(CreatedModifiedMixin, TrackChangesMixin, models.Model):
'tags': data.get('tags'), 'tags': data.get('tags'),
} }
def download_name(self) -> str:
"""Return a file name for downloads."""
version = self.version.first()
replace_char = f'{version}'.replace('.', '-')
return f'{utils.slugify(replace_char)}.zip'
def download_url(self, platform=None, append_repository_and_compatibility=True) -> str:
filename = self.download_name()
version = self.version.first()
if platform:
download_url = reverse(
'extensions:version-platform-download',
kwargs={
'type_slug': version.extension.type_slug,
'slug': version.extension.slug,
'version': version.version,
'platform': platform,
'filename': filename,
},
)
else:
download_url = reverse(
'extensions:version-download',
kwargs={
'type_slug': version.extension.type_slug,
'slug': version.extension.slug,
'version': version.version,
'filename': filename,
},
)
if append_repository_and_compatibility:
params = {
'repository': '/api/v1/extensions/',
'blender_version_min': version.blender_version_min,
}
if version.blender_version_max:
params['blender_version_max'] = version.blender_version_max
if platforms := self.platforms():
params['platforms'] = ','.join(platforms)
query_string = urlencode(params)
download_url += f'?{query_string}'
return download_url
def get_thumbnail_of_size(self, size_key: str) -> str: def get_thumbnail_of_size(self, size_key: str) -> str:
"""Return absolute path portion of the URL of a thumbnail of this file. """Return absolute path portion of the URL of a thumbnail of this file.

View File

@ -82,7 +82,7 @@ class TestTasks(TestCase):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
response = self.client.get(version.extension.get_versions_url()) response = self.client.get(version.extension.get_versions_url())
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
response = self.client.get(version.files.first().download_url()) response = self.client.get(version.download_url())
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual( self.assertEqual(
response['Location'], response['Location'],