Skip to content

Commit

Permalink
Merge pull request #11871 from rtibbles/0.16intodevelop
Browse files Browse the repository at this point in the history
0.16 into develop
  • Loading branch information
rtibbles authored Feb 21, 2024
2 parents 0a25fd5 + ca9bd42 commit 9f6898b
Show file tree
Hide file tree
Showing 296 changed files with 6,960 additions and 6,186 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/pr_build_kolibri.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ jobs:
deb:
name: Build DEB file
needs: whl
uses: learningequality/kolibri-installer-debian/.github/workflows/build_deb.yml@master
uses: learningequality/kolibri-installer-debian/.github/workflows/build_deb.yml@v0.16.1
with:
tar-file-name: ${{ needs.whl.outputs.tar-file-name }}
ref: master
ref: v0.16.1
exe:
name: Build EXE file
needs: whl
uses: learningequality/kolibri-installer-windows/.github/workflows/build_exe.yml@develop
uses: learningequality/kolibri-installer-windows/.github/workflows/build_exe.yml@v1.6.0
with:
whl-file-name: ${{ needs.whl.outputs.whl-file-name }}
ref: develop
ref: v1.6.0
apk:
name: Build APK file
needs: whl
uses: learningequality/kolibri-installer-android/.github/workflows/build_apk.yml@develop
uses: learningequality/kolibri-installer-android/.github/workflows/build_apk.yml@v0.1.0
with:
tar-file-name: ${{ needs.whl.outputs.tar-file-name }}
ref: develop
ref: v0.1.0
24 changes: 12 additions & 12 deletions .github/workflows/release_kolibri.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jobs:
dmg:
name: Build DMG file
needs: whl
uses: learningequality/kolibri-app/.github/workflows/build_mac.yml@main
uses: learningequality/kolibri-app/.github/workflows/build_mac.yml@v0.4.0
with:
whl-file-name: ${{ needs.whl.outputs.whl-file-name }}
release: true
ref: main
ref: v0.4.0
secrets:
KOLIBRI_MAC_APP_IDENTITY: ${{ secrets.KOLIBRI_MAC_APP_IDENTITY }}
KOLIBRI_MAC_APP_CERTIFICATE: ${{ secrets.KOLIBRI_MAC_APP_CERTIFICATE }}
Expand All @@ -56,10 +56,10 @@ jobs:
deb:
name: Build DEB file
needs: whl
uses: learningequality/kolibri-installer-debian/.github/workflows/build_deb.yml@master
uses: learningequality/kolibri-installer-debian/.github/workflows/build_deb.yml@v0.16.1
with:
tar-file-name: ${{ needs.whl.outputs.tar-file-name }}
ref: master
ref: v0.16.1
upload_deb:
uses: ./.github/workflows/upload_github_release_asset.yml
needs: deb
Expand All @@ -69,11 +69,11 @@ jobs:
exe:
name: Build EXE file
needs: whl
uses: learningequality/kolibri-installer-windows/.github/workflows/build_exe.yml@develop
uses: learningequality/kolibri-installer-windows/.github/workflows/build_exe.yml@v1.6.0
with:
whl-file-name: ${{ needs.whl.outputs.whl-file-name }}
release: true
ref: develop
ref: v1.6.0
secrets:
KOLIBRI_WINDOWS_INSTALLER_CERTIFICATE: ${{ secrets.KOLIBRI_WINDOWS_INSTALLER_CERTIFICATE }}
KOLIBRI_WINDOWS_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.KOLIBRI_WINDOWS_INSTALLER_CERTIFICATE_PASSWORD }}
Expand All @@ -86,10 +86,10 @@ jobs:
zip:
name: Build Raspberry Pi Image
needs: deb
uses: learningequality/pi-gen/.github/workflows/build_zip.yml@master
uses: learningequality/pi-gen/.github/workflows/build_zip.yml@v0.16.0
with:
deb-file-name: ${{ needs.deb.outputs.deb-file-name }}
ref: master
ref: v0.16.0
upload_zip:
uses: ./.github/workflows/upload_github_release_asset.yml
needs: zip
Expand All @@ -99,11 +99,11 @@ jobs:
apk:
name: Build Android APK
needs: whl
uses: learningequality/kolibri-installer-android/.github/workflows/build_apk.yml@develop
uses: learningequality/kolibri-installer-android/.github/workflows/build_apk.yml@v0.1.0
with:
tar-file-name: ${{ needs.whl.outputs.tar-file-name }}
release: true
ref: develop
ref: v0.1.0
secrets:
KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE: ${{ secrets.KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE }}
KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE_PASSWORD: ${{ secrets.KOLIBRI_ANDROID_APP_PRODUCTION_KEYSTORE_PASSWORD }}
Expand Down Expand Up @@ -196,9 +196,9 @@ jobs:
name: Release Android App
if: ${{ !github.event.release.prerelease }}
needs: [apk, block_release_step]
uses: learningequality/kolibri-installer-android/.github/workflows/release_apk.yml@develop
uses: learningequality/kolibri-installer-android/.github/workflows/release_apk.yml@v0.1.0
with:
version-code: ${{ needs.apk.outputs.version-code }}
ref: develop
ref: v0.1.0
secrets:
KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON: ${{ secrets.KOLIBRI_ANDROID_PLAY_STORE_API_SERVICE_ACCOUNT_JSON }}
59 changes: 59 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,65 @@

List of the most important changes for each release.


## 0.16.0

### Features

#### Robust syncing of user data and resources
##### Support for quick learner setup and independent learners
- Kolibri has a new onboarding experience which allows joining a facility, and streamlines getting started as an independent learner with a rapid “on my own setup” option
- Independent learners can transfer their existing data and learning progress to a facility.
##### Resource discovery
- Assigned lesson and quiz resources are now automatically transferred to learner devices, allowing coaches to dynamically manage learner content, rather than an administrator needing to import all content devices before distribution.
- Administrators and independent learners are now able to view other Kolibri Libraries on their local network and browse their resources, without having to import content. If they are connected to the internet, they will be able to browse resources on the Kolibri Content Library (hosted on Kolibri Studio).
- Administrators can allow learners to download resources from other Kolibri Libraries to their device to view within Kolibri, even when they are no longer on the same network.
##### Support for administrators
- Administrators have a new option to add a PIN on learner-only devices, which allows an administrator easy access to the Device page while preventing learners from inadvertently making changes.
- Administrators are now able to schedule syncing of facility data on a recurring basis at custom intervals.
- When exporting log files, administrators are able to select the date range for the logs.
##### Practice quizzes
- This release supports practice quizzes, which are resources in the format of quizzes that learners can take in preparation for an assessment. They are able to see their score, and retry as many times as they would like, independently. Practice quiz resources are available through the Library, or can be assigned as part of a lesson. The same questions can also be assigned as a coach assigned quiz as a standardized assessment.

### Changes

#### Dev documentation/dev updates
- Updated node version to 18
- Getting started documentation updated
- Updated to Webpack 5
- Created Github actions for build pipeline
- Created Github action to add assets to PRs
- Task API changes have been finalized after initial work in 0.15. Documentation is now updated to describe how to interact with the API and define tasks in plugins.

#### Architectural changes
- There is a new page architecture that is used across all Kolibri plugins, and the component has been removed. (Selected relevant high level issues and PRs: #9102, #9128, 9134.)
- The Kolibri Process Bus has been updated to support easier composability for custom deployment architectures.
- Conditional promises have been removed.
- To support the new onboarding process for Kolibri, Kolibri apps can now access a capability to provide access controls based on the currently active operating system user.

#### API Breaking Changes
- Tasks API has now been finalized, previous methods for interacting with tasks that do not use the pluggable Tasks API have been removed.
- The drive info endpoint has been moved the into the device app but functionality remains the same
- The API for coordinating learner only device synchronization within a local area network has been updated to ensure robust and reliable syncing. Any users wishing to use learner only device synchronization must update all Kolibri devices to this newer version

#### API Additions (non-breaking changes)
- REST API for enabling and disabling plugins
- Add API endpoint and hook driven capability for UI initiated device restart
- Public signup viewset
- Public content metadata endpoints to support granular resource import

#### Accessibility improvements
- Landmarks have been added and refined across the Library page and its related subpages, for better accessibility. This is a first step in support of more robust accessibility support, particularly in terms of page navigation for screen reader users.

### Deprecations
- Support for Python 2.7 will be dropped in the upcoming version, 0.17. Upgrade your Python version to Python 3.6+ to continue working with Kolibri. More recent versions of Python 3 are recommended.
- Support for this Internet Explorer 11 will be dropped in the upcoming version, 0.17. We recommend installing other browsers, such as Mozilla Firefox or Google Chrome, in order to continue working with Kolibri.

### Kolibry Design System upgrades
- Kolibri is now using kolibri-design-system v2.0.0 (a major version upgrade!). Please see the KDS release's Github page for documentation and full details about breaking changes and new features.



## 0.15.12

### Added
Expand Down
2 changes: 1 addition & 1 deletion kolibri/core/assets/src/views/SyncStatusDescription.vue
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
},
queuedDescription: {
message: 'The device is waiting to sync.',
context: 'Description of the device syncing status.',
context: 'Description of the device syncing status',
},
unableOrNoSyncDescription: {
message:
Expand Down
16 changes: 14 additions & 2 deletions kolibri/core/auth/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
from kolibri.core.device.utils import allow_guest_access
from kolibri.core.device.utils import allow_other_browsers_to_connect
from kolibri.core.device.utils import APP_AUTH_TOKEN_COOKIE_NAME
from kolibri.core.device.utils import is_full_facility_import
from kolibri.core.device.utils import valid_app_key_on_request
from kolibri.core.logger.models import UserSessionLog
from kolibri.core.mixins import BulkCreateMixin
Expand Down Expand Up @@ -180,6 +181,10 @@ class Meta:
fields = ["facility_id"]


def _is_full_facility_import(dataset):
return is_full_facility_import(dataset["id"])


class FacilityDatasetViewSet(ValuesViewset):
permission_classes = (KolibriAuthPermissions,)
filter_backends = (
Expand All @@ -205,7 +210,10 @@ class FacilityDatasetViewSet(ValuesViewset):
"preset",
)

field_map = {"allow_guest_access": lambda x: allow_guest_access()}
field_map = {
"allow_guest_access": lambda x: allow_guest_access(),
"is_full_facility_import": _is_full_facility_import,
}

def get_queryset(self):
return FacilityDataset.objects.filter(
Expand Down Expand Up @@ -772,7 +780,11 @@ class SignUpViewSet(viewsets.GenericViewSet, CreateModelMixin):
serializer_class = FacilityUserSerializer

def check_can_signup(self, serializer):
if not serializer.validated_data["facility"].dataset.learner_can_sign_up:
facility = serializer.validated_data["facility"]
if (
not facility.dataset.learner_can_sign_up
or not facility.dataset.full_facility_import
):
raise PermissionDenied("Cannot sign up to this facility")

def perform_create(self, serializer):
Expand Down
9 changes: 3 additions & 6 deletions kolibri/core/auth/kolibri_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ def handle_initial(self, context):
from kolibri.core.device.utils import device_provisioned

if context.is_receiver and device_provisioned():
is_pull = context.is_pull
is_push = context.is_push
pull = context.is_pull
push = context.is_push
sync_filter = str(context.filter)

instance_kwargs = {}
Expand All @@ -51,10 +51,7 @@ def handle_initial(self, context):

cleanupsync.enqueue(
kwargs=dict(
is_pull=is_pull,
is_push=is_push,
sync_filter=sync_filter,
**instance_kwargs
pull=pull, push=push, sync_filter=sync_filter, **instance_kwargs
)
)

Expand Down
7 changes: 7 additions & 0 deletions kolibri/core/auth/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,13 @@ def reset_to_default_settings(self, preset=None):
setattr(self, key, value)
self.save()

@cached_property
def full_facility_import(self):
"""
Returns True if this user is a member of a facility that has been fully imported.
"""
return is_full_facility_import(self.id)


class AbstractFacilityDataModel(FacilityDataSyncableModel):
"""
Expand Down
14 changes: 6 additions & 8 deletions kolibri/core/auth/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,20 +613,18 @@ def deletefacility(facility):


class CleanUpSyncsValidator(JobValidator):
is_pull = serializers.BooleanField(required=False)
is_push = serializers.BooleanField(required=False)
pull = serializers.BooleanField(required=False)
push = serializers.BooleanField(required=False)
sync_filter = serializers.CharField(required=True)
client_instance_id = HexOnlyUUIDField(required=False)
server_instance_id = HexOnlyUUIDField(required=False)

def validate(self, data):
if data.get("is_pull") is None and data.get("is_push") is None:
if data.get("pull") is None and data.get("push") is None:
raise serializers.ValidationError("Either pull or push must be specified")
elif data.get("pull") is data.get("push"):
raise serializers.ValidationError(
"Either is_pull or is_push must be specified"
)
elif data.get("is_pull") is data.get("is_push"):
raise serializers.ValidationError(
"Only one of is_pull or is_push needs to be specified"
"Only one of pull or push needs to be specified"
)

if (
Expand Down
26 changes: 12 additions & 14 deletions kolibri/core/auth/test/test_auth_tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,26 +802,24 @@ class CleanUpSyncsTaskValidatorTestCase(TestCase):
def setUp(self):
self.kwargs = dict(
type=cleanupsync.__name__,
is_push=True,
is_pull=False,
push=True,
pull=False,
sync_filter=uuid4().hex,
client_instance_id=uuid4().hex,
)

def test_validator__no_push_no_pull(self):
self.kwargs.pop("is_push")
self.kwargs.pop("is_pull")
self.kwargs.pop("push")
self.kwargs.pop("pull")
validator = CleanUpSyncsValidator(data=self.kwargs)
with self.assertRaisesRegex(
serializers.ValidationError, "Either is_pull or is_push"
):
with self.assertRaisesRegex(serializers.ValidationError, "Either pull or push"):
validator.is_valid(raise_exception=True)

def test_validator__both_push_and_pull(self):
self.kwargs.update(is_pull=True)
self.kwargs.update(pull=True)
validator = CleanUpSyncsValidator(data=self.kwargs)
with self.assertRaisesRegex(
serializers.ValidationError, "Only one of is_pull or is_push"
serializers.ValidationError, "Only one of pull or push"
):
validator.is_valid(raise_exception=True)

Expand Down Expand Up @@ -853,8 +851,8 @@ def test_validator__no_sync_filter(self):
class CleanUpSyncsTaskTestCase(TestCase):
def setUp(self):
self.kwargs = dict(
is_push=True,
is_pull=False,
push=True,
pull=False,
sync_filter=uuid4().hex,
client_instance_id=uuid4().hex,
)
Expand All @@ -876,8 +874,8 @@ def test_calls_command(self, mock_call_command):
mock_call_command.assert_called_with(
"cleanupsyncs",
expiration=1,
is_push=self.kwargs["is_push"],
is_pull=self.kwargs["is_pull"],
push=self.kwargs["push"],
pull=self.kwargs["pull"],
sync_filter=self.kwargs["sync_filter"],
client_instance_id=self.kwargs["client_instance_id"],
)
Expand All @@ -903,7 +901,7 @@ def _create_sync(self, last_activity_timestamp=None, client_instance_id=None):
id=uuid4().hex,
active=True,
sync_session=sync_session,
push=self.kwargs["is_push"],
push=self.kwargs["push"],
filter=self.kwargs["sync_filter"],
last_activity_timestamp=last_activity_timestamp,
)
Expand Down
8 changes: 4 additions & 4 deletions kolibri/core/auth/test/test_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def test_handle_initial__is_server(self, mock_task):
self.assertFalse(result)
mock_task.enqueue.assert_called_once_with(
kwargs=dict(
is_pull=self.context.is_pull,
is_push=self.context.is_push,
pull=self.context.is_pull,
push=self.context.is_push,
sync_filter=str(self.context.filter),
client_instance_id=self.context.sync_session.client_instance_id.hex,
)
Expand All @@ -53,8 +53,8 @@ def test_handle_initial__not_server(self, mock_task):
self.assertFalse(result)
mock_task.enqueue.assert_called_once_with(
kwargs=dict(
is_pull=self.context.is_pull,
is_push=self.context.is_push,
pull=self.context.is_pull,
push=self.context.is_push,
sync_filter=str(self.context.filter),
server_instance_id=self.context.sync_session.server_instance_id.hex,
)
Expand Down
Loading

0 comments on commit 9f6898b

Please sign in to comment.