kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
Merge branch 'license-metadata-update' into 'develop'
License metadata update See merge request funkwhale/funkwhale!712merge-requests/757/head
commit
8687a64873
|
@ -114,7 +114,14 @@ class UpdateMutationSerializer(serializers.ModelSerializer, MutationSerializer):
|
|||
# to ensure we store ids instead of model instances in our json
|
||||
# payload
|
||||
for field, attr in self.serialized_relations.items():
|
||||
data[field] = getattr(data[field], attr)
|
||||
try:
|
||||
obj = data[field]
|
||||
except KeyError:
|
||||
continue
|
||||
if obj is None:
|
||||
data[field] = None
|
||||
else:
|
||||
data[field] = getattr(obj, attr)
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
|
|
|
@ -1,6 +1,29 @@
|
|||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.pagination import PageNumberPagination, _positive_int
|
||||
|
||||
|
||||
class FunkwhalePagination(PageNumberPagination):
|
||||
page_size_query_param = "page_size"
|
||||
max_page_size = 50
|
||||
default_max_page_size = 50
|
||||
default_page_size = None
|
||||
view = None
|
||||
|
||||
def paginate_queryset(self, queryset, request, view=None):
|
||||
self.view = view
|
||||
return super().paginate_queryset(queryset, request, view)
|
||||
|
||||
def get_page_size(self, request):
|
||||
max_page_size = (
|
||||
getattr(self.view, "max_page_size", 0) or self.default_max_page_size
|
||||
)
|
||||
page_size = getattr(self.view, "default_page_size", 0) or max_page_size
|
||||
if self.page_size_query_param:
|
||||
try:
|
||||
return _positive_int(
|
||||
request.query_params[self.page_size_query_param],
|
||||
strict=True,
|
||||
cutoff=max_page_size,
|
||||
)
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
|
||||
return page_size
|
||||
|
|
|
@ -21,4 +21,4 @@ class TrackMutationSerializer(mutations.UpdateMutationSerializer):
|
|||
|
||||
class Meta:
|
||||
model = models.Track
|
||||
fields = ["license", "title", "position"]
|
||||
fields = ["license", "title", "position", "copyright"]
|
||||
|
|
|
@ -524,6 +524,7 @@ class LicenseViewSet(viewsets.ReadOnlyModelViewSet):
|
|||
serializer_class = serializers.LicenseSerializer
|
||||
queryset = models.License.objects.all().order_by("code")
|
||||
lookup_value_regex = ".*"
|
||||
max_page_size = 1000
|
||||
|
||||
def get_queryset(self):
|
||||
# ensure our licenses are up to date in DB
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import pytest
|
||||
|
||||
from funkwhale_api.common import pagination
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"view_max_page_size, view_default_page_size, request_page_size, expected",
|
||||
[
|
||||
(50, 50, None, 50),
|
||||
(50, 25, None, 25),
|
||||
(25, None, None, 25),
|
||||
(50, 25, 100, 50),
|
||||
(50, None, 100, 50),
|
||||
(50, 25, 33, 33),
|
||||
],
|
||||
)
|
||||
def test_funkwhale_pagination_uses_view_page_size(
|
||||
view_max_page_size, view_default_page_size, request_page_size, expected, mocker
|
||||
):
|
||||
p = pagination.FunkwhalePagination()
|
||||
|
||||
p.view = mocker.Mock(
|
||||
max_page_size=view_max_page_size, default_page_size=view_default_page_size
|
||||
)
|
||||
query = {}
|
||||
if request_page_size:
|
||||
query["page_size"] = request_page_size
|
||||
request = mocker.Mock(query_params=query)
|
||||
assert p.get_page_size(request) == expected
|
|
@ -13,6 +13,18 @@ def test_track_license_mutation(factories, now):
|
|||
assert track.license.code == "cc-by-sa-4.0"
|
||||
|
||||
|
||||
def test_track_null_license_mutation(factories, now):
|
||||
track = factories["music.Track"](license="cc-by-sa-4.0")
|
||||
mutation = factories["common.Mutation"](
|
||||
type="update", target=track, payload={"license": None}
|
||||
)
|
||||
licenses.load(licenses.LICENSES)
|
||||
mutation.apply()
|
||||
track.refresh_from_db()
|
||||
|
||||
assert track.license is None
|
||||
|
||||
|
||||
def test_track_title_mutation(factories, now):
|
||||
track = factories["music.Track"](title="foo")
|
||||
mutation = factories["common.Mutation"](
|
||||
|
@ -24,6 +36,17 @@ def test_track_title_mutation(factories, now):
|
|||
assert track.title == "bar"
|
||||
|
||||
|
||||
def test_track_copyright_mutation(factories, now):
|
||||
track = factories["music.Track"](copyright="foo")
|
||||
mutation = factories["common.Mutation"](
|
||||
type="update", target=track, payload={"copyright": "bar"}
|
||||
)
|
||||
mutation.apply()
|
||||
track.refresh_from_db()
|
||||
|
||||
assert track.copyright == "bar"
|
||||
|
||||
|
||||
def test_track_position_mutation(factories):
|
||||
track = factories["music.Track"](position=4)
|
||||
mutation = factories["common.Mutation"](
|
||||
|
|
|
@ -612,7 +612,7 @@ def test_list_licenses(api_client, preferences, mocker):
|
|||
|
||||
expected = [
|
||||
serializers.LicenseSerializer(l.conf).data
|
||||
for l in models.License.objects.order_by("code")[:25]
|
||||
for l in models.License.objects.order_by("code")
|
||||
]
|
||||
url = reverse("api:v1:licenses-list")
|
||||
|
||||
|
|
|
@ -59,10 +59,28 @@
|
|||
<label :for="fieldConfig.id">{{ fieldConfig.label }}</label>
|
||||
<input :type="fieldConfig.inputType || 'text'" v-model="values[fieldConfig.id]" :required="fieldConfig.required" :name="fieldConfig.id" :id="fieldConfig.id">
|
||||
</template>
|
||||
<template v-else-if="fieldConfig.type === 'license'">
|
||||
<label :for="fieldConfig.id">{{ fieldConfig.label }}</label>
|
||||
|
||||
<select
|
||||
ref="license"
|
||||
v-model="values[fieldConfig.id]"
|
||||
:required="fieldConfig.required"
|
||||
:id="fieldConfig.id"
|
||||
class="ui fluid search dropdown">
|
||||
<option :value="null"><translate translate-context="*/*/*">N/A</translate></option>
|
||||
<option v-for="license in licenses" :key="license.code" :value="license.code">{{ license.name}}</option>
|
||||
</select>
|
||||
<button class="ui tiny basic left floated button" form="noop" @click.prevent="values[fieldConfig.id] = null">
|
||||
<i class="x icon"></i>
|
||||
<translate translate-context="Content/Library/Button.Label">Clear</translate>
|
||||
</button>
|
||||
|
||||
</template>
|
||||
<div v-if="values[fieldConfig.id] != initialValues[fieldConfig.id]">
|
||||
<button class="ui tiny basic right floated reset button" form="noop" @click.prevent="values[fieldConfig.id] = initialValues[fieldConfig.id]">
|
||||
<i class="undo icon"></i>
|
||||
<translate translate-context="Content/Library/Button.Label" :translate-params="{value: initialValues[fieldConfig.id]}">Reset to initial value: %{ value }</translate>
|
||||
<translate translate-context="Content/Library/Button.Label" :translate-params="{value: initialValues[fieldConfig.id] || ''}">Reset to initial value: %{ value }</translate>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -87,6 +105,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import $ from 'jquery'
|
||||
import _ from '@/lodash'
|
||||
import axios from "axios"
|
||||
import EditList from '@/components/library/EditList'
|
||||
|
@ -94,7 +113,7 @@ import EditCard from '@/components/library/EditCard'
|
|||
import edits from '@/edits'
|
||||
|
||||
export default {
|
||||
props: ["objectType", "object"],
|
||||
props: ["objectType", "object", "licenses"],
|
||||
components: {
|
||||
EditList,
|
||||
EditCard
|
||||
|
@ -113,6 +132,9 @@ export default {
|
|||
created () {
|
||||
this.setValues()
|
||||
},
|
||||
mounted() {
|
||||
$(".ui.dropdown").dropdown({fullTextSearch: true})
|
||||
},
|
||||
computed: {
|
||||
configs: edits.getConfigs,
|
||||
config: edits.getConfig,
|
||||
|
@ -182,6 +204,15 @@ export default {
|
|||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'values.license' (newValue) {
|
||||
if (newValue === null) {
|
||||
$(this.$refs.license).dropdown('clear')
|
||||
} else {
|
||||
$(this.$refs.license).dropdown('set selected', newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -6,9 +6,17 @@
|
|||
<translate v-if="canEdit" key="1" translate-context="Content/*/Title">Edit this track</translate>
|
||||
<translate v-else key="2" translate-context="Content/*/Title">Suggest an edit on this track</translate>
|
||||
</h2>
|
||||
<edit-form :object-type="objectType" :object="object" :can-edit="canEdit"></edit-form>
|
||||
<edit-form
|
||||
v-if="!isLoadingLicenses"
|
||||
:object-type="objectType"
|
||||
:object="object"
|
||||
:can-edit="canEdit"
|
||||
:licenses="licenses"></edit-form>
|
||||
<div v-else class="ui inverted active dimmer">
|
||||
<div class="ui loader"></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -19,12 +27,27 @@ export default {
|
|||
props: ["objectType", "object", "libraries"],
|
||||
data() {
|
||||
return {
|
||||
id: this.object.id
|
||||
id: this.object.id,
|
||||
isLoadingLicenses: false,
|
||||
licenses: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
EditForm
|
||||
},
|
||||
created () {
|
||||
this.fetchLicenses()
|
||||
},
|
||||
methods: {
|
||||
fetchLicenses () {
|
||||
let self = this
|
||||
self.isLoadingLicenses = true
|
||||
axios.get('licenses/').then((response) => {
|
||||
self.isLoadingLicenses = false
|
||||
self.licenses = response.data.results
|
||||
})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
canEdit () {
|
||||
return true
|
||||
|
|
|
@ -10,13 +10,6 @@ export default {
|
|||
label: this.$pgettext('Content/Track/*/Noun', 'Title'),
|
||||
getValue: (obj) => { return obj.title }
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/*/*/Noun', 'License'),
|
||||
getValue: (obj) => { return obj.license }
|
||||
},
|
||||
{
|
||||
id: 'position',
|
||||
type: 'text',
|
||||
|
@ -24,7 +17,21 @@ export default {
|
|||
required: false,
|
||||
label: this.$pgettext('*/*/*/Short, Noun', 'Position'),
|
||||
getValue: (obj) => { return obj.position }
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'copyright',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/Track/*/Noun', 'Copyright'),
|
||||
getValue: (obj) => { return obj.copyright }
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
type: 'license',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/*/*/Noun', 'License'),
|
||||
getValue: (obj) => { return obj.license },
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue