Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d4e579a
reset pagination for trashModal
ozer550 Nov 13, 2025
1b2c0b7
push new modififed field to sync
ozer550 Nov 24, 2025
db37082
Add double click blocking and error handling for user creation form s…
rtibbles Dec 1, 2025
52a27d5
make changes in test to reflect new modified field operational changes
ozer550 Dec 2, 2025
821c2f0
Merge pull request #5552 from ozer550/fix-trash-page-pagination
rtibbles Dec 4, 2025
0dc4909
First pass, based off of suggestions from AI.
DXCanas Dec 4, 2025
ff6209a
Removing vestigial git module definition
DXCanas Dec 4, 2025
c14ec2b
Removing symlink to prod dockerfile.
DXCanas Dec 4, 2025
840dcad
Clearing out prober code.
DXCanas Dec 4, 2025
aae5494
Moving nginx files out of `deploy` and housing them with other image-…
DXCanas Dec 4, 2025
200f537
WIP: Moving prod dockerfiles out of defunct k8s dir.
DXCanas Dec 5, 2025
1dbd0db
WIP: Flattening out new image structure, following current naming sta…
DXCanas Dec 5, 2025
c4155bf
Removing defunct cloudbuild-pr.yaml
DXCanas Dec 5, 2025
cb8cdfe
Flattening file structure further.
DXCanas Dec 5, 2025
0c39da8
Bringing in temporary symlinks for infra-side CD.
DXCanas Dec 5, 2025
1394aba
Cleaning up more prober code.
DXCanas Dec 5, 2025
57ef5cd
Merge pull request #5596 from DXCanas/studio-infra-file-cleanup
rtibbles Dec 16, 2025
b50bdb9
Configure Celery for graceful shutdown
claude Oct 30, 2025
8ca219b
Update paths for nginx Dockerfile in workflow
rtibbles Dec 17, 2025
502c99e
Merge pull request #5612 from learningequality/rtibbles-patch-1
bjester Dec 17, 2025
f8b957f
Merge pull request #5520 from learningequality/claude/issue-5000-hotf…
rtibbles Dec 18, 2025
eb50847
Fix Language foreign key column lengths (#5618)
claude Dec 19, 2025
19f4152
Merge pull request #5582 from learningequality/claude/fix-submissions…
rtibbles Jan 6, 2026
f05c8e0
Fix sync missing fields: language, provider, aggregator, role_visibility
claude Nov 29, 2025
b6f40a9
Merge pull request #5590 from learningequality/claude/issue-4930-plan…
bjester Jan 8, 2026
1208fee
Narrow Language FK fix to only included_languages M2M table
rtibbles Jan 12, 2026
4f67e69
Merge pull request #5623 from rtibbles/m2m_lang
bjester Jan 13, 2026
f174386
Upgrade le-utils to version 0.2.14
rtibbles Jan 7, 2026
5890cf8
Add test for and update publish logic to publish pre/post tests for U…
rtibbles Jan 7, 2026
0ad254f
Make topic completion criterion validation more rigorous.
rtibbles Jan 20, 2026
2d7d22c
Run completion criterion checking on all node types.
rtibbles Jan 20, 2026
9564806
Temporarily hide draft button.
rtibbles Jan 20, 2026
2d234d4
Add strong requirement for UNIT folders to require preposttest comple…
rtibbles Jan 20, 2026
8b9be86
Merge pull request #5646 from rtibbles/unit_tests
marcellamaki Jan 20, 2026
2506bf5
Merge pull request #5652 from rtibbles/hide_draft
bjester Jan 21, 2026
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
4 changes: 2 additions & 2 deletions .github/workflows/containerbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ jobs:
with:
skip_after_successful_duplicate: false
github_token: ${{ github.token }}
paths: '["k8s/images/nginx/*", ".github/workflows/containerbuild.yml"]'
paths: '["docker/Dockerfile.nginx.prod", "docker/nginx/*", ".github/workflows/containerbuild.yml"]'

build_nginx:
name: nginx - test build of nginx Docker image
Expand All @@ -100,6 +100,6 @@ jobs:
uses: docker/build-push-action@v6
with:
context: ./
file: ./k8s/images/nginx/Dockerfile
file: ./docker/Dockerfile.nginx.prod
platforms: linux/amd64
push: false
6 changes: 0 additions & 6 deletions .gitmodules

This file was deleted.

6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,7 @@ dcbuild:
$(DOCKER_COMPOSE) build

dcup: .docker/minio .docker/postgres
# run all services except for cloudprober
$(DOCKER_COMPOSE) up studio-app celery-worker

dcup-cloudprober: .docker/minio .docker/postgres
# run all services including cloudprober
# run all services
$(DOCKER_COMPOSE) up

dcdown:
Expand Down
102 changes: 0 additions & 102 deletions cloudbuild-pr.yaml

This file was deleted.

99 changes: 0 additions & 99 deletions cloudbuild-production.yaml

This file was deleted.

35 changes: 32 additions & 3 deletions contentcuration/contentcuration/constants/completion_criteria.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
from jsonschema.validators import validator_for
from le_utils.constants import completion_criteria
from le_utils.constants import content_kinds
from le_utils.constants import exercises
from le_utils.constants import mastery_criteria
from le_utils.constants import modalities


def _build_validator():
Expand Down Expand Up @@ -52,10 +54,11 @@ def _build_validator():
completion_criteria.APPROX_TIME,
completion_criteria.REFERENCE,
},
content_kinds.TOPIC: {completion_criteria.MASTERY},
}


def check_model_for_kind(data, kind):
def check_model_for_kind(data, kind, modality=None):
model = data.get("model")
if kind is None or model is None or kind not in ALLOWED_MODELS_PER_KIND:
return
Expand All @@ -68,11 +71,37 @@ def check_model_for_kind(data, kind):
)
)

if kind == content_kinds.TOPIC:
check_topic_completion_criteria(data, modality)

def validate(data, kind=None):

def check_topic_completion_criteria(data, modality):
"""
Validates topic-specific completion criteria rules:
- Topics can only have completion criteria if modality is UNIT
- Topics can only use PRE_POST_TEST mastery model
"""
# Topics can only have completion criteria with UNIT modality
if modality != modalities.UNIT:
raise ValidationError(
"Topics can only have completion criteria with UNIT modality"
)

# Topics can only use PRE_POST_TEST mastery model
threshold = data.get("threshold", {})
mastery_model = threshold.get("mastery_model")
if mastery_model is not None and mastery_model != exercises.PRE_POST_TEST:
raise ValidationError(
"mastery_model '{}' is invalid for topic content kind; "
"only '{}' is allowed".format(mastery_model, exercises.PRE_POST_TEST)
)


def validate(data, kind=None, modality=None):
"""
:param data: Dictionary of data to validate
:param kind: A str of the node content kind
:param modality: A str of the node modality (required for topics with completion criteria)
:raises: ValidationError: When invalid
"""
# empty dicts are okay
Expand Down Expand Up @@ -104,4 +133,4 @@ def validate(data, kind=None):
e.error_list.extend(error_descriptions)
raise e

check_model_for_kind(data, kind)
check_model_for_kind(data, kind, modality)
Original file line number Diff line number Diff line change
Expand Up @@ -213,9 +213,10 @@
<KButton
primary
class="mt-5"
:disabled="offline"
:disabled="offline || submitting"
:text="$tr('finishButton')"
type="submit"
data-test="submit-button"
/>
</VForm>
</VLayout>
Expand Down Expand Up @@ -260,6 +261,7 @@
return {
valid: true,
registrationFailed: false,
submitting: false,
form: {
first_name: '',
last_name: '',
Expand Down Expand Up @@ -482,6 +484,12 @@
// We need to check the "acceptedAgreement" here explicitly because it is not a
// Vuetify form field and does not trigger the form validation.
if (this.$refs.form.validate() && this.acceptedAgreement) {
// Prevent double submission
if (this.submitting) {
return Promise.resolve();
}

this.submitting = true;
const cleanedData = this.clean(this.form);
return this.register(cleanedData)
.then(() => {
Expand Down Expand Up @@ -517,6 +525,9 @@
this.registrationFailed = true;
this.valid = false;
}
})
.finally(() => {
this.submitting = false;
});
} else if (this.$refs.top.scrollIntoView) {
this.$refs.top.scrollIntoView({ behavior: 'smooth' });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,19 @@ describe('create', () => {
expect(wrapper.vm.registrationFailed).toBe(true);
});
});
describe('double-submit prevention', () => {
it('should prevent multiple API calls on rapid clicks', async () => {
const [wrapper, mocks] = await makeWrapper();

// Click submit multiple times
const p1 = wrapper.vm.submit();
const p2 = wrapper.vm.submit();
const p3 = wrapper.vm.submit();

await Promise.all([p1, p2, p3]);

// Only 1 API call should be made
expect(mocks.register).toHaveBeenCalledTimes(1);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@
</VBtn>

<VBtn
v-if="false"
color="primary"
data-test="display-publish-draft-dialog-btn"
@click="displayPublishDraftDialog = true"
Expand Down
Loading