diff --git a/apps/flutter_parent/assets/svg/canvas-parent-login-logo-dark.svg b/apps/flutter_parent/assets/svg/canvas-parent-login-logo-dark.svg
new file mode 100644
index 0000000000..ab80d41cfa
--- /dev/null
+++ b/apps/flutter_parent/assets/svg/canvas-parent-login-logo-dark.svg
@@ -0,0 +1,10 @@
+
diff --git a/apps/flutter_parent/assets/svg/canvas-parent-login-logo.svg b/apps/flutter_parent/assets/svg/canvas-parent-login-logo.svg
index a6413426ae..b595560d0c 100644
--- a/apps/flutter_parent/assets/svg/canvas-parent-login-logo.svg
+++ b/apps/flutter_parent/assets/svg/canvas-parent-login-logo.svg
@@ -1,10 +1,10 @@
diff --git a/apps/flutter_parent/flutter_parent_sdk_url b/apps/flutter_parent/flutter_parent_sdk_url
new file mode 100644
index 0000000000..6fcfede9b9
--- /dev/null
+++ b/apps/flutter_parent/flutter_parent_sdk_url
@@ -0,0 +1 @@
+https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_2.5.3-stable.tar.xz
\ No newline at end of file
diff --git a/apps/flutter_parent/lib/l10n/app_localizations.dart b/apps/flutter_parent/lib/l10n/app_localizations.dart
index 26fa996e0b..4872b5fe70 100644
--- a/apps/flutter_parent/lib/l10n/app_localizations.dart
+++ b/apps/flutter_parent/lib/l10n/app_localizations.dart
@@ -1705,4 +1705,7 @@ class AppLocalizations {
String get aboutVersionTitle =>
Intl.message('Version', desc: 'Title for Version field on about page');
+
+ String get aboutLogoSemanticsLabel =>
+ Intl.message('Instructure logo', desc: 'Semantics label for the Instructure logo on the about page');
}
diff --git a/apps/flutter_parent/lib/l10n/res/intl_en.arb b/apps/flutter_parent/lib/l10n/res/intl_en.arb
index 9d2ca6f103..d5edbf3ece 100644
--- a/apps/flutter_parent/lib/l10n/res/intl_en.arb
+++ b/apps/flutter_parent/lib/l10n/res/intl_en.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2023-04-14T11:04:46.988317",
+ "@@last_modified": "2023-08-25T11:04:20.901151",
"alertsLabel": "Alerts",
"@alertsLabel": {
"description": "The label for the Alerts tab",
@@ -2742,5 +2742,12 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
+ },
+ "Instructure logo": "Instructure logo",
+ "@Instructure logo": {
+ "description": "Semantics label for the Instructure logo on the about page",
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/apps/flutter_parent/lib/l10n/res/intl_messages.arb b/apps/flutter_parent/lib/l10n/res/intl_messages.arb
index 9d2ca6f103..d5edbf3ece 100644
--- a/apps/flutter_parent/lib/l10n/res/intl_messages.arb
+++ b/apps/flutter_parent/lib/l10n/res/intl_messages.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2023-04-14T11:04:46.988317",
+ "@@last_modified": "2023-08-25T11:04:20.901151",
"alertsLabel": "Alerts",
"@alertsLabel": {
"description": "The label for the Alerts tab",
@@ -2742,5 +2742,12 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
+ },
+ "Instructure logo": "Instructure logo",
+ "@Instructure logo": {
+ "description": "Semantics label for the Instructure logo on the about page",
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
}
}
\ No newline at end of file
diff --git a/apps/flutter_parent/lib/models/alert.dart b/apps/flutter_parent/lib/models/alert.dart
index 78d3d4caf1..6f3d04ff2d 100644
--- a/apps/flutter_parent/lib/models/alert.dart
+++ b/apps/flutter_parent/lib/models/alert.dart
@@ -109,6 +109,22 @@ abstract class Alert implements Built {
int index2 = htmlUrl.lastIndexOf('/discussion_topics');
return htmlUrl.substring(index1, index2);
}
+
+ String getCourseIdForGradeAlerts() {
+ if (alertType == AlertType.courseGradeLow || alertType == AlertType.courseGradeHigh) {
+ return contextId;
+ } else if (alertType == AlertType.assignmentGradeLow || alertType == AlertType.assignmentGradeHigh) {
+ return _getCourseIdFromUrl();
+ } else {
+ return null;
+ }
+ }
+
+ String _getCourseIdFromUrl() {
+ RegExp regex = RegExp(r'/courses/(\d+)/');
+ Match match = regex.firstMatch(htmlUrl);
+ return (match != null && match.groupCount >= 1) ? match.group(1) : null;
+ }
}
/// If you need to change the values sent over the wire when serializing you
diff --git a/apps/flutter_parent/lib/models/assignment.dart b/apps/flutter_parent/lib/models/assignment.dart
index ce3d4d27dd..30b7f756a7 100644
--- a/apps/flutter_parent/lib/models/assignment.dart
+++ b/apps/flutter_parent/lib/models/assignment.dart
@@ -185,6 +185,10 @@ abstract class Assignment implements Built {
bool get isDiscussion => submissionTypes.contains(SubmissionTypes.discussionTopic);
bool get isQuiz => submissionTypes.contains(SubmissionTypes.onlineQuiz);
+
+ bool isGradingTypeQuantitative() {
+ return gradingType == GradingType.points || gradingType == GradingType.percent;
+ }
}
@BuiltValueEnum(wireName: 'grading_type')
diff --git a/apps/flutter_parent/lib/models/course.dart b/apps/flutter_parent/lib/models/course.dart
index b1bd3fa47a..931a1bc5b0 100644
--- a/apps/flutter_parent/lib/models/course.dart
+++ b/apps/flutter_parent/lib/models/course.dart
@@ -15,7 +15,10 @@ library course;
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
+import 'package:built_value/json_object.dart';
import 'package:built_value/serializer.dart';
+import 'package:flutter_parent/models/course_settings.dart';
+import 'package:flutter_parent/models/grading_scheme_item.dart';
import 'package:flutter_parent/models/section.dart';
import 'package:flutter_parent/models/term.dart';
@@ -123,6 +126,19 @@ abstract class Course implements Built {
@nullable
BuiltList get sections;
+ @nullable
+ CourseSettings get settings;
+
+ @nullable
+ @BuiltValueField(wireName: 'grading_scheme')
+ BuiltList get gradingScheme;
+
+ List get gradingSchemeItems {
+ if (gradingScheme == null) return [];
+ return gradingScheme.map((item) => GradingSchemeItem.fromJson(item)).where((element) => element != null).toList()
+ ..sort((a, b) => b.value.compareTo(a.value));
+ }
+
static void _initializeBuilder(CourseBuilder b) => b
..id = ''
..enrollments = ListBuilder()
@@ -172,6 +188,12 @@ abstract class Course implements Built {
bool isValidForCurrentStudent(String currentStudentId) {
return enrollments?.any((enrollment) => enrollment.userId == currentStudentId) ?? false;
}
+
+ String convertScoreToLetterGrade(double score, double maxScore) {
+ if (maxScore == 0.0 || gradingSchemeItems.isEmpty) return "";
+ double percent = score / maxScore;
+ return gradingSchemeItems.firstWhere((element) => percent >= element.value, orElse: () => gradingSchemeItems.last).grade;
+ }
}
@BuiltValueEnum(wireName: 'default_view')
diff --git a/apps/flutter_parent/lib/models/course.g.dart b/apps/flutter_parent/lib/models/course.g.dart
index 9efff7de24..737bb7026e 100644
--- a/apps/flutter_parent/lib/models/course.g.dart
+++ b/apps/flutter_parent/lib/models/course.g.dart
@@ -87,77 +87,81 @@ class _$CourseSerializer implements StructuredSerializer {
serializers.serialize(object.restrictEnrollmentsToCourseDates,
specifiedType: const FullType(bool)),
];
- result.add('original_name');
- if (object.originalName == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.originalName,
- specifiedType: const FullType(String)));
- }
- result.add('course_code');
- if (object.courseCode == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.courseCode,
- specifiedType: const FullType(String)));
- }
- result.add('start_at');
- if (object.startAt == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.startAt,
+ Object value;
+ value = object.originalName;
+
+ result
+ ..add('original_name')
+ ..add(
+ serializers.serialize(value, specifiedType: const FullType(String)));
+ value = object.courseCode;
+
+ result
+ ..add('course_code')
+ ..add(
+ serializers.serialize(value, specifiedType: const FullType(String)));
+ value = object.startAt;
+
+ result
+ ..add('start_at')
+ ..add(serializers.serialize(value,
specifiedType: const FullType(DateTime)));
- }
- result.add('end_at');
- if (object.endAt == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.endAt,
+ value = object.endAt;
+
+ result
+ ..add('end_at')
+ ..add(serializers.serialize(value,
specifiedType: const FullType(DateTime)));
- }
- result.add('syllabus_body');
- if (object.syllabusBody == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.syllabusBody,
- specifiedType: const FullType(String)));
- }
- result.add('image_download_url');
- if (object.imageDownloadUrl == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.imageDownloadUrl,
- specifiedType: const FullType(String)));
- }
- result.add('workflow_state');
- if (object.workflowState == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.workflowState,
- specifiedType: const FullType(String)));
- }
- result.add('default_view');
- if (object.homePage == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.homePage,
+ value = object.syllabusBody;
+
+ result
+ ..add('syllabus_body')
+ ..add(
+ serializers.serialize(value, specifiedType: const FullType(String)));
+ value = object.imageDownloadUrl;
+
+ result
+ ..add('image_download_url')
+ ..add(
+ serializers.serialize(value, specifiedType: const FullType(String)));
+ value = object.workflowState;
+
+ result
+ ..add('workflow_state')
+ ..add(
+ serializers.serialize(value, specifiedType: const FullType(String)));
+ value = object.homePage;
+
+ result
+ ..add('default_view')
+ ..add(serializers.serialize(value,
specifiedType: const FullType(HomePage)));
- }
- result.add('term');
- if (object.term == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.term,
- specifiedType: const FullType(Term)));
- }
- result.add('sections');
- if (object.sections == null) {
- result.add(null);
- } else {
- result.add(serializers.serialize(object.sections,
+ value = object.term;
+
+ result
+ ..add('term')
+ ..add(serializers.serialize(value, specifiedType: const FullType(Term)));
+ value = object.sections;
+
+ result
+ ..add('sections')
+ ..add(serializers.serialize(value,
specifiedType:
const FullType(BuiltList, const [const FullType(Section)])));
- }
+ value = object.settings;
+
+ result
+ ..add('settings')
+ ..add(serializers.serialize(value,
+ specifiedType: const FullType(CourseSettings)));
+ value = object.gradingScheme;
+
+ result
+ ..add('grading_scheme')
+ ..add(serializers.serialize(value,
+ specifiedType:
+ const FullType(BuiltList, const [const FullType(JsonObject)])));
+
return result;
}
@@ -170,8 +174,7 @@ class _$CourseSerializer implements StructuredSerializer {
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
- final dynamic value = iterator.current;
- if (value == null) continue;
+ final Object value = iterator.current;
switch (key) {
case 'id':
result.id = serializers.deserialize(value,
@@ -265,6 +268,16 @@ class _$CourseSerializer implements StructuredSerializer {
BuiltList, const [const FullType(Section)]))
as BuiltList