Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions admin/src/api/sdk.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1763,6 +1763,7 @@ export const adminEventKompomaattiEntriesCreate = <ThrowOnError extends boolean
unknown,
ThrowOnError
>({
...formDataBodySerializer,
responseType: "json",
security: [
{
Expand All @@ -1778,7 +1779,7 @@ export const adminEventKompomaattiEntriesCreate = <ThrowOnError extends boolean
url: "/api/v2/admin/event/{event_pk}/kompomaatti/entries/",
...options,
headers: {
"Content-Type": "application/json",
"Content-Type": null,
...options.headers,
},
});
Expand Down Expand Up @@ -1859,6 +1860,7 @@ export const adminEventKompomaattiEntriesPartialUpdate = <ThrowOnError extends b
unknown,
ThrowOnError
>({
...formDataBodySerializer,
responseType: "json",
security: [
{
Expand All @@ -1874,7 +1876,7 @@ export const adminEventKompomaattiEntriesPartialUpdate = <ThrowOnError extends b
url: "/api/v2/admin/event/{event_pk}/kompomaatti/entries/{id}/",
...options,
headers: {
"Content-Type": "application/json",
"Content-Type": null,
...options.headers,
},
});
Expand All @@ -1894,6 +1896,7 @@ export const adminEventKompomaattiEntriesUpdate = <ThrowOnError extends boolean
unknown,
ThrowOnError
>({
...formDataBodySerializer,
responseType: "json",
security: [
{
Expand All @@ -1909,7 +1912,7 @@ export const adminEventKompomaattiEntriesUpdate = <ThrowOnError extends boolean
url: "/api/v2/admin/event/{event_pk}/kompomaatti/entries/{id}/",
...options,
headers: {
"Content-Type": "application/json",
"Content-Type": null,
...options.headers,
},
});
Expand Down Expand Up @@ -1960,6 +1963,7 @@ export const adminEventKompomaattiEntriesReorderCreate = <ThrowOnError extends b
unknown,
ThrowOnError
>({
...formDataBodySerializer,
responseType: "json",
security: [
{
Expand All @@ -1975,7 +1979,7 @@ export const adminEventKompomaattiEntriesReorderCreate = <ThrowOnError extends b
url: "/api/v2/admin/event/{event_pk}/kompomaatti/entries/reorder/",
...options,
headers: {
"Content-Type": "application/json",
"Content-Type": null,
...options.headers,
},
});
Expand Down Expand Up @@ -2194,7 +2198,10 @@ export const adminEventKompomaattiLiveVotingRevealAllCreate = <
};

/**
* Staff viewset for managing live voting state during a compo presentation.
* Reveal a single entry. Only the next unrevealed entry (by order_index) can be revealed.
*
* Note: Entry.objects.filter().update() is used instead of entry.save() throughout
* this viewset to avoid triggering Entry.save()'s generate_alternates() side effect.
*/
export const adminEventKompomaattiLiveVotingRevealEntryCreate = <
ThrowOnError extends boolean = false,
Expand Down
33 changes: 17 additions & 16 deletions admin/src/api/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,10 @@ export type Compo = {
* Source size limit
*/
source_sizelimit?: number;
/**
* Image file size limit
*/
imagefile_sizelimit?: number;
/**
* Allowed file extensions
*/
Expand All @@ -259,6 +263,7 @@ export type Compo = {
image_formats?: string;
readonly max_source_size: number;
readonly max_entry_size: number;
readonly max_image_size: number;
readonly source_format_list: Array<string>;
readonly entry_format_list: Array<string>;
readonly image_format_list: Array<string>;
Expand Down Expand Up @@ -316,10 +321,6 @@ export type CompoEntry = {
* Revealed in live voting
*/
live_voting_revealed?: boolean;
/**
* Live voting reveal order
*/
live_voting_order?: number;
disqualified?: boolean;
/**
* Disqualification reason
Expand Down Expand Up @@ -372,10 +373,6 @@ export type CompoEntryRequest = {
* Revealed in live voting
*/
live_voting_revealed?: boolean;
/**
* Live voting reveal order
*/
live_voting_order?: number;
disqualified?: boolean;
/**
* Disqualification reason
Expand Down Expand Up @@ -421,6 +418,10 @@ export type CompoRequest = {
* Source size limit
*/
source_sizelimit?: number;
/**
* Image file size limit
*/
imagefile_sizelimit?: number;
/**
* Allowed file extensions
*/
Expand Down Expand Up @@ -1573,10 +1574,6 @@ export type PatchedCompoEntryRequest = {
* Revealed in live voting
*/
live_voting_revealed?: boolean;
/**
* Live voting reveal order
*/
live_voting_order?: number;
disqualified?: boolean;
/**
* Disqualification reason
Expand Down Expand Up @@ -1622,6 +1619,10 @@ export type PatchedCompoRequest = {
* Source size limit
*/
source_sizelimit?: number;
/**
* Image file size limit
*/
imagefile_sizelimit?: number;
/**
* Allowed file extensions
*/
Expand Down Expand Up @@ -3368,6 +3369,10 @@ export type CompoWritable = {
* Source size limit
*/
source_sizelimit?: number;
/**
* Image file size limit
*/
imagefile_sizelimit?: number;
/**
* Allowed file extensions
*/
Expand Down Expand Up @@ -3428,10 +3433,6 @@ export type CompoEntryWritable = {
* Revealed in live voting
*/
live_voting_revealed?: boolean;
/**
* Live voting reveal order
*/
live_voting_order?: number;
disqualified?: boolean;
/**
* Disqualification reason
Expand Down
4 changes: 3 additions & 1 deletion admin/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
"votingEnd": "Voting end",
"entrySizelimit": "Max entry size (bytes)",
"sourceSizelimit": "Max source size (bytes)",
"imageSizelimit": "Max image size (bytes)",
"formats": "Entry formats (e.g. png|jpg)",
"sourceFormats": "Source formats",
"imageFormats": "Image formats",
Expand Down Expand Up @@ -425,7 +426,8 @@
"youtubeUrl": "YouTube URL",
"disqualifiedOn": "Entry is disqualified",
"disqualifiedOff": "Entry is not disqualified",
"disqualifiedReason": "Reason"
"disqualifiedReason": "Reason",
"skipSizeCheck": "Override file size limits"
},
"noAlternateFiles": "No alternate files available."
},
Expand Down
4 changes: 3 additions & 1 deletion admin/src/locales/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
"votingEnd": "Äänestyksen päättymisaika",
"entrySizelimit": "Tuotoksen enimmäiskoko (tavua)",
"sourceSizelimit": "Lähdekoodin enimmäiskoko (tavua)",
"imageSizelimit": "Kuvatiedoston enimmäiskoko (tavua)",
"formats": "Tuotosmuodot (esim. png|jpg)",
"sourceFormats": "Lähdekoodimuodot",
"imageFormats": "Kuvamuodot",
Expand Down Expand Up @@ -425,7 +426,8 @@
"youtubeUrl": "YouTube-URL",
"disqualifiedOn": "Tuotos on hylätty",
"disqualifiedOff": "Tuotosta ei ole hylätty",
"disqualifiedReason": "Syy"
"disqualifiedReason": "Syy",
"skipSizeCheck": "Ohita tiedostojen kokorajat"
},
"noAlternateFiles": "Vaihtoehtoisia tiedostoja ei ole saatavilla."
},
Expand Down
17 changes: 15 additions & 2 deletions admin/src/views/kompomaatti/CompoEditView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,27 @@
{{ t("CompoEditView.sections.fileSettings") }}
</FormSection>
<v-row>
<v-col cols="12" md="6">
<v-col cols="12" md="4">
<FileSizeInputField
v-model="entrySizelimit.value.value"
:error-message="entrySizelimit.errorMessage.value"
:label="t('CompoEditView.labels.entrySizelimit')"
/>
</v-col>
<v-col cols="12" md="6">
<v-col cols="12" md="4">
<FileSizeInputField
v-model="sourceSizelimit.value.value"
:error-message="sourceSizelimit.errorMessage.value"
:label="t('CompoEditView.labels.sourceSizelimit')"
/>
</v-col>
<v-col cols="12" md="4">
<FileSizeInputField
v-model="imageSizelimit.value.value"
:error-message="imageSizelimit.errorMessage.value"
:label="t('CompoEditView.labels.imageSizelimit')"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="4">
Expand Down Expand Up @@ -255,6 +262,7 @@ const API_FIELD_MAPPING: FieldMapping = {
voting_end: "votingEnd",
entry_sizelimit: "entrySizelimit",
source_sizelimit: "sourceSizelimit",
imagefile_sizelimit: "imageSizelimit",
formats: "formats",
source_formats: "sourceFormats",
image_formats: "imageFormats",
Expand Down Expand Up @@ -358,6 +366,7 @@ const validationSchema = yupObject({
votingEnd: yupString().required(),
entrySizelimit: yupNumber().nullable(),
sourceSizelimit: yupNumber().nullable(),
imageSizelimit: yupNumber().nullable(),
formats: yupString(),
sourceFormats: yupString(),
imageFormats: yupString(),
Expand All @@ -380,6 +389,7 @@ const { handleSubmit, setValues, setErrors, meta } = useForm({
votingEnd: "",
entrySizelimit: null as number | null,
sourceSizelimit: null as number | null,
imageSizelimit: null as number | null,
formats: "",
sourceFormats: "",
imageFormats: "",
Expand All @@ -400,6 +410,7 @@ const compoStart = useField<string>("compoStart");
const votingEnd = useField<string>("votingEnd");
const entrySizelimit = useField<number | null>("entrySizelimit");
const sourceSizelimit = useField<number | null>("sourceSizelimit");
const imageSizelimit = useField<number | null>("imageSizelimit");
const formats = useField<string>("formats");
const sourceFormats = useField<string>("sourceFormats");
const imageFormats = useField<string>("imageFormats");
Expand Down Expand Up @@ -448,6 +459,7 @@ function buildBody(values: GenericObject) {
voting_end: toISODatetime(values.votingEnd)!,
entry_sizelimit: values.entrySizelimit,
source_sizelimit: values.sourceSizelimit,
imagefile_sizelimit: values.imageSizelimit,
formats: values.formats || "",
source_formats: values.sourceFormats || "",
image_formats: values.imageFormats || "",
Expand Down Expand Up @@ -510,6 +522,7 @@ onMounted(async () => {
votingEnd: toLocalDatetime(item.voting_end),
entrySizelimit: item.entry_sizelimit ?? null,
sourceSizelimit: item.source_sizelimit ?? null,
imageSizelimit: item.imagefile_sizelimit ?? null,
formats: item.formats ?? "",
sourceFormats: item.source_formats ?? "",
imageFormats: item.image_formats ?? "",
Expand Down
14 changes: 12 additions & 2 deletions admin/src/views/kompomaatti/EntryEditView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@
<FormSection>
{{ t("EntryEditView.sections.files") }}
</FormSection>
<v-checkbox
v-model="skipSizeCheck"
:label="t('EntryEditView.labels.skipSizeCheck')"
density="compact"
hide-details
class="mb-2"
/>
<v-row>
<v-col cols="12" md="4">
<FileUploadField
Expand Down Expand Up @@ -250,6 +257,7 @@ const { getEventById } = useEvents();

const loading = ref(false);
const saving = ref(false);
const skipSizeCheck = ref(false);
const entryName = ref<string>("");
const eventId = computed(() => parseInt(props.eventId, 10));
const isEditMode = computed(() => props.id !== undefined);
Expand Down Expand Up @@ -438,7 +446,8 @@ async function createItem(values: GenericObject) {
// Type assertion needed: our bodySerializer handles null for file clearing
body: body as api.CompoEntryRequest,
bodySerializer: () => toFormData(body),
});
...(skipSizeCheck.value && { query: { skip_size_check: "true" } }),
} as Parameters<typeof api.adminEventKompomaattiEntriesCreate>[0]);
toast.success(t("EntryEditView.createSuccess"));
return true;
} catch (e) {
Expand All @@ -455,7 +464,8 @@ async function editItem(itemId: number, values: GenericObject) {
// Type assertion needed: our bodySerializer handles null for file clearing
body: body as api.PatchedCompoEntryRequest,
bodySerializer: () => toFormData(body),
});
...(skipSizeCheck.value && { query: { skip_size_check: "true" } }),
} as Parameters<typeof api.adminEventKompomaattiEntriesPartialUpdate>[0]);
toast.success(t("EntryEditView.editSuccess"));
return true;
} catch (e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class CompoSerializer(ModelSerializer[Compo]):

max_entry_size = IntegerField(read_only=True)
max_source_size = IntegerField(read_only=True)
max_image_size = IntegerField(read_only=True)
source_format_list = ListField(child=CharField(), read_only=True)
entry_format_list = ListField(child=CharField(), read_only=True)
image_format_list = ListField(child=CharField(), read_only=True)
Expand All @@ -26,11 +27,13 @@ class Meta:
"voting_end",
"entry_sizelimit",
"source_sizelimit",
"imagefile_sizelimit",
"formats",
"source_formats",
"image_formats",
"max_source_size",
"max_entry_size",
"max_image_size",
"source_format_list",
"entry_format_list",
"image_format_list",
Expand Down
14 changes: 11 additions & 3 deletions backend/Instanssi/api/v2/utils/entry_file_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@
from Instanssi.kompomaatti.models import Compo, Entry


def validate_entry_files(data: dict[str, Any], compo: Compo, instance: Entry | None = None) -> None:
def validate_entry_files(
data: dict[str, Any],
compo: Compo,
instance: Entry | None = None,
*,
skip_size_check: bool = False,
) -> None:
"""Validate entry files against compo settings.

Checks file format (extension), file size, and image file requirements.
Expand Down Expand Up @@ -51,7 +57,7 @@ def validate_entry_files(data: dict[str, Any], compo: Compo, instance: Entry | N
errors = {}
for key, args in check_files_on.items():
if file := data.get(key):
field_errors = _validate_file(file, *args)
field_errors = _validate_file(file, *args, skip_size_check=skip_size_check)
if field_errors:
errors[key] = field_errors

Expand All @@ -72,12 +78,14 @@ def _validate_file(
accept_formats_readable: str,
max_size: int,
max_readable_size: str,
*,
skip_size_check: bool = False,
) -> list[str]:
"""Validate file size and format, returning list of error messages."""
errors: list[str] = []

# Check file size
if file.size is not None and file.size > max_size:
if not skip_size_check and file.size is not None and file.size > max_size:
errors.append(_("Maximum allowed file size is %(size)s") % {"size": max_readable_size})

# Check file extension — use all suffixes so that e.g. ".tar.gz" is
Expand Down
Loading