diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e40e0765..30d96142 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -384,7 +384,7 @@ SPEC CHECKSUMS: FirebaseCoreExtension: 2dbc745b337eb99d2026a7a309ae037bd873f45e FirebaseCoreInternal: 26233f705cc4531236818a07ac84d20c333e505a FirebaseCrashlytics: a83f26fb922a3fe181eb738fb4dcf0c92bba6455 - FirebaseFirestore: 58d92e3cc19fd1c93e279d466aaf148ce6dbf635 + FirebaseFirestore: 9a14eef134fd586d8200f4dfa0ee3cb093a05d8f FirebaseInstallations: b822f91a61f7d1ba763e5ccc9d4f2e6f2ed3b3ee FirebaseMessaging: 0c0ae1eb722ef0c07f7801e5ded8dccd1357d6d4 FirebaseRemoteConfig: 64b6ada098c649304114a817effd7e5f87229b11 diff --git a/lib/generated/intl/messages_all.dart b/lib/generated/intl/messages_all.dart index 1771ed2d..c65333cc 100644 --- a/lib/generated/intl/messages_all.dart +++ b/lib/generated/intl/messages_all.dart @@ -19,10 +19,10 @@ import 'package:intl/src/intl_helpers.dart'; import 'messages_en.dart' as messages_en; import 'messages_zh_TW.dart' as messages_zh_tw; -typedef Future LibraryLoader(); +typedef LibraryLoader = Future Function(); Map _deferredLibraries = { - 'en': () => new SynchronousFuture(null), - 'zh_TW': () => new SynchronousFuture(null), + 'en': () => SynchronousFuture(null), + 'zh_TW': () => SynchronousFuture(null), }; MessageLookupByLibrary? _findExact(String localeName) { @@ -41,13 +41,13 @@ Future initializeMessages(String localeName) { var availableLocale = Intl.verifiedLocale(localeName, (locale) => _deferredLibraries[locale] != null, onFailure: (_) => null); if (availableLocale == null) { - return new SynchronousFuture(false); + return SynchronousFuture(false); } var lib = _deferredLibraries[availableLocale]; - lib == null ? new SynchronousFuture(false) : lib(); - initializeInternalMessageLookup(() => new CompositeMessageLookup()); + lib == null ? SynchronousFuture(false) : lib(); + initializeInternalMessageLookup(() => CompositeMessageLookup()); messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor); - return new SynchronousFuture(true); + return SynchronousFuture(true); } bool _messagesExistFor(String locale) { diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index b57a618d..4c49f218 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -13,9 +13,9 @@ import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; -final messages = new MessageLookup(); +final messages = MessageLookup(); -typedef String MessageIfAbsent(String messageStr, List args); +typedef MessageIfAbsent = String Function(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; diff --git a/lib/generated/intl/messages_zh_TW.dart b/lib/generated/intl/messages_zh_TW.dart index c6d61bf8..c2085ef7 100644 --- a/lib/generated/intl/messages_zh_TW.dart +++ b/lib/generated/intl/messages_zh_TW.dart @@ -13,9 +13,9 @@ import 'package:intl/intl.dart'; import 'package:intl/message_lookup_by_library.dart'; -final messages = new MessageLookup(); +final messages = MessageLookup(); -typedef String MessageIfAbsent(String messageStr, List args); +typedef MessageIfAbsent = String Function(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'zh_TW'; diff --git a/lib/src/connector/course_connector.dart b/lib/src/connector/course_connector.dart index aa92a42d..e3d735d5 100644 --- a/lib/src/connector/course_connector.dart +++ b/lib/src/connector/course_connector.dart @@ -1,62 +1,33 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 +// ignore_for_file: import_of_legacy_library_into_null_safe + import 'package:dio/dio.dart'; import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/connector/core/connector.dart'; import 'package:flutter_app/src/connector/core/connector_parameter.dart'; import 'package:flutter_app/src/connector/ntut_connector.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:flutter_app/src/model/course/course_main_extra_json.dart'; +import 'package:flutter_app/src/model/course/course_semester.dart'; import 'package:flutter_app/src/model/course/course_score_json.dart'; -import 'package:flutter_app/src/model/course/course_syllabus_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; +import 'package:flutter_app/src/model/coursetable/course.dart'; +import 'package:flutter_app/src/model/coursetable/user.dart'; +import 'package:flutter_app/src/model/course/course_syllabus.dart'; import 'package:html/dom.dart'; import 'package:html/parser.dart'; enum CourseConnectorStatus { loginSuccess, loginFail, unknownError } -class CourseMainInfo { - List json; - String studentName; -} - class CourseConnector { static const _ssoLoginUrl = "${NTUTConnector.host}ssoIndex.do"; static const String _courseCNHost = "https://aps.ntut.edu.tw/course/tw/"; static const String _courseENHost = "https://aps.ntut.edu.tw/course/en/"; static const String _postCourseCNUrl = "${_courseCNHost}Select.jsp"; static const String _getSyllabusCNUrl = "${_courseCNHost}ShowSyllabus.jsp"; - static const String _postTeacherCourseCNUrl = "${_courseCNHost}Teach.jsp"; static const String _postCourseENUrl = "${_courseENHost}Select.jsp"; static const String _creditUrl = "${_courseCNHost}Cprog.jsp"; static Future login() async { - String result; try { - ConnectorParameter parameter; - Document tagNode; - List nodes; - Map data = { - "apUrl": "https://aps.ntut.edu.tw/course/tw/courseSID.jsp", - "apOu": "aa_0010-", - "sso": "true", - "datetime1": DateTime.now().millisecondsSinceEpoch.toString() - }; - parameter = ConnectorParameter(_ssoLoginUrl); - parameter.data = data; - result = await Connector.getDataByGet(parameter); - tagNode = parse(result); - nodes = tagNode.getElementsByTagName("input"); - data = {}; - for (Element node in nodes) { - String name = node.attributes['name']; - String value = node.attributes['value']; - data[name] = value; - } - String jumpUrl = tagNode.getElementsByTagName("form")[0].attributes["action"]; - parameter = ConnectorParameter(jumpUrl); - parameter.data = data; - await Connector.getDataByPostResponse(parameter); + Document tagNode = await _getSSORedirectNodesInLoginPhase(); + await _tryToSSOLoginOrThrowException(_getSSOLoginJumpUrl(tagNode), _getSSOLoginPayload(tagNode)); return CourseConnectorStatus.loginSuccess; } catch (e, stack) { Log.eWithStack(e.toString(), stack); @@ -64,101 +35,135 @@ class CourseConnector { } } - static Future getCourseENName(String url) async { - try { - ConnectorParameter parameter; - Document tagNode; - Element node; - parameter = ConnectorParameter(url); - parameter.charsetName = 'big5'; - String result = await Connector.getDataByGet(parameter); - tagNode = parse(result); - node = tagNode.getElementsByTagName("table").first; - node = node.getElementsByTagName("tr")[1]; - return node.getElementsByTagName("td")[2].text.replaceAll(RegExp(r"\n"), ""); - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - return null; + static Future> getEnglishCourses(String studentId, int year, int semester) async { + final ConnectorParameter parameter = ConnectorParameter(_postCourseENUrl); + final Map data = { + "code": studentId, + "format": "-2", + "year": year.toString(), + "sem": semester.toString(), + }; + parameter.charsetName = 'utf-8'; + parameter.data = data; + final String result = await Connector.getDataByGet(parameter); + + final Document tagNode = parse(result); + final List courseRows = tagNode.getElementsByTagName("table")[1].getElementsByTagName("tr"); + final List courses = []; + + for (int rowIndex = 1; rowIndex < courseRows.length - 1; rowIndex++) { + final courseRowData = courseRows[rowIndex].getElementsByTagName("td"); + courses.add( + Course.parseNodeString( + idString: courseRowData[0].text, + name: courseRowData[1].text, + stageString: "", + creditString: courseRowData[2].text, + periodCountString: courseRowData[3].text, + category: "", + teacherString: courseRowData[4].innerHtml.replaceAll("
", "\n"), + classNameString: courseRowData[5].text, + periodSlots: courseRowData.sublist(6, 13).map((data) => data.text).toList(), + classroomString: courseRowData[13].text, + applyStatus: courseRowData[14].text, + language: courseRowData[15].text, + syllabusLink: "", + note: courseRowData[16].text, + ), + ); } + + return courses; } - static Future getCourseExtraInfo(String courseId) async { + static Future getUserInfo(String studentId, int year, int semester) async { try { - ConnectorParameter parameter; - Document tagNode; - Element node; - List courseNodes, nodes, classExtraInfoNodes; - Map data = { - "code": courseId, - "format": "-1", + final ConnectorParameter parameter = ConnectorParameter(_postCourseCNUrl); + final Map data = { + "code": studentId, + "format": "-2", + "year": year.toString(), + "sem": semester.toString(), }; - parameter = ConnectorParameter(_postCourseCNUrl); + parameter.charsetName = 'utf-8'; parameter.data = data; - String result = await Connector.getDataByPost(parameter); - tagNode = parse(result); - courseNodes = tagNode.getElementsByTagName("table"); - - CourseExtraInfoJson courseExtraInfo = CourseExtraInfoJson(); - - //取得學期資料 - nodes = courseNodes[0].getElementsByTagName("td"); - SemesterJson semester = SemesterJson(); - - // Previously, the title string of the first course table was stored separately in its `` element, - // but it currently stores all the information in a row, - // e.g. "學號:110310144  姓名:xxx  班級:電機三甲    112 學年度 第 1 學期 上課時間表" - // so the RegExp is used to filter out only the number parts - final titleString = nodes[0].text; - final RegExp studentSemesterDetailFilter = RegExp(r'\b[\dA-Z]+\b'); - final Iterable studentSemesterDetailMatches = studentSemesterDetailFilter.allMatches(titleString); - // "studentSemesterDetails" should consist of three numerical values - // ex: [110310144, 112, 1] - final List studentSemesterDetails = studentSemesterDetailMatches.map((match) => match.group(0)).toList(); - if (studentSemesterDetails.isEmpty) { - throw RangeError("[TAT] course_connector.dart: studentSemesterDetails list is empty"); - } - if (studentSemesterDetails.length < 3) { - throw RangeError("[TAT] course_connector.dart: studentSemesterDetails list has range less than 3"); - } - semester.year = studentSemesterDetails[1]; - semester.semester = studentSemesterDetails[2]; + final String result = await Connector.getDataByGet(parameter); - courseExtraInfo.courseSemester = semester; + final Document tagNode = parse(result); + final String courseTableHead = tagNode.getElementsByTagName("table")[1].getElementsByTagName("tr").first.text; + final List matches = RegExp(r".+?:(.+?)\s").allMatches(courseTableHead).toList(); - CourseExtraJson courseExtra = CourseExtraJson(); + final String? name = matches[1].group(1); + final String? className = matches[2].group(1); - nodes = courseNodes[1].getElementsByTagName("tr"); - final List courseIds = nodes.skip(2).map((node) => node.getElementsByTagName("td")[0].text).toList(); - final courseIdPosition = courseIds.indexWhere((element) => element.contains(courseId)); - if (courseIdPosition == -1) { - throw StateError('[TAT] course_connector.dart: CourseId not found: $courseId'); - } else { - node = nodes[courseIdPosition + 2]; + if (name == null) { + throw Exception("getUserInfo: Cannot Fetch the user info (null name)."); } - classExtraInfoNodes = node.getElementsByTagName("td"); - courseExtra.id = strQ2B(classExtraInfoNodes[0].text).replaceAll(RegExp(r"\s"), ""); - courseExtra.name = classExtraInfoNodes[1].getElementsByTagName("a")[0].text; - courseExtra.openClass = classExtraInfoNodes[7].getElementsByTagName("a")[0].text; - - // if the courseExtraInfo.herf (課程大綱連結) is empty, - // the category of the course will be set to ▲ (校訂專業必修) as default - if (classExtraInfoNodes[18].text.trim() != "" && - classExtraInfoNodes[18].getElementsByTagName("a")[0].attributes.containsKey("href")) { - courseExtra.href = _courseCNHost + classExtraInfoNodes[18].getElementsByTagName("a")[0].attributes["href"]; - parameter = ConnectorParameter(courseExtra.href); - result = await Connector.getDataByPost(parameter); - tagNode = parse(result); - nodes = tagNode.getElementsByTagName("tr"); - courseExtra.category = nodes[1].getElementsByTagName("td")[6].text; - } else { - courseExtra.category = constCourseType[4]; + + if (className == null) { + throw Exception("getUserInfo: Cannot Fetch the user info (null className)."); } - courseExtra.selectNumber = "s?"; - courseExtra.withdrawNumber = "w?"; + return User(id: studentId, name: name, className: className); + } catch (e, stack) { + Log.eWithStack(e.toString(), stack); + return User.origin(); + } + } + + static Future> getChineseCourses(String studentId, int year, int semester) async { + final ConnectorParameter parameter = ConnectorParameter(_postCourseCNUrl); + final Map data = { + "code": studentId, + "format": "-2", + "year": year.toString(), + "sem": semester.toString(), + }; + parameter.charsetName = 'utf-8'; + parameter.data = data; + final String result = await Connector.getDataByGet(parameter); + final Document tagNode = parse(result); + final List courseRows = tagNode.getElementsByTagName("table")[1].getElementsByTagName("tr"); + final List courses = []; + + for (int rowIndex = 2; rowIndex < courseRows.length - 1; rowIndex++) { + final courseRowData = courseRows[rowIndex].getElementsByTagName("td"); + final syllabusNode = courseRowData[18].getElementsByTagName("a"); + final syllabusLinkHref = syllabusNode.isEmpty ? null : syllabusNode.first.attributes["href"]; + courses.add( + Course.parseNodeString( + idString: courseRowData[0].text, + name: courseRowData[1].text, + stageString: courseRowData[2].text, + creditString: courseRowData[3].text, + periodCountString: courseRowData[4].text, + category: courseRowData[5].text, + teacherString: courseRowData[6].text, + classNameString: courseRowData[7].text, + periodSlots: courseRowData.sublist(8, 15).map((data) => data.text).toList(), + classroomString: courseRowData[15].text, + applyStatus: courseRowData[16].text, + language: courseRowData[17].text, + syllabusLink: syllabusLinkHref == null ? "" : _postCourseCNUrl + syllabusLinkHref, + note: courseRowData[19].text, + ), + ); + } + + return courses; + } - courseExtraInfo.course = courseExtra; - return courseExtraInfo; + static Future getCourseENName(String url) async { + try { + final ConnectorParameter parameter = ConnectorParameter(url); + parameter.charsetName = 'big5'; + final String result = await Connector.getDataByGet(parameter); + + final Document tagNode = parse(result); + final Element node = + tagNode.getElementsByTagName("table").first.getElementsByTagName("tr")[1].getElementsByTagName("td")[2]; + + return node.text.replaceAll(RegExp(r"\n"), ""); } catch (e, stack) { Log.eWithStack(e.toString(), stack); return null; @@ -175,23 +180,24 @@ class CourseConnector { String result = await Connector.getDataByGet(parameter); Document tagNode = parse(result); - var tables = tagNode.getElementsByTagName("table"); - var trs = tables[0].getElementsByTagName("tr"); - var syllabusRow = trs[1].getElementsByTagName("td"); - - var model = CourseSyllabusJson( - yearSemester: syllabusRow[0].text, - courseId: syllabusRow[1].text, - courseName: syllabusRow[2].text, - phase: syllabusRow[3].text, - credit: syllabusRow[4].text, - hour: syllabusRow[5].text, - category: syllabusRow[6].text, - teachers: syllabusRow[7].text, - className: syllabusRow[8].text, - applyStudentCount: syllabusRow[9].text, - withdrawStudentCount: syllabusRow[10].text, - note: syllabusRow[11].text); + final tables = tagNode.getElementsByTagName("table"); + final trs = tables[0].getElementsByTagName("tr"); + final syllabusRow = trs[1].getElementsByTagName("td"); + + final model = CourseSyllabusJson( + yearSemester: syllabusRow[0].text, + courseId: syllabusRow[1].text, + courseName: syllabusRow[2].text, + phase: syllabusRow[3].text, + credit: syllabusRow[4].text, + hour: syllabusRow[5].text, + category: syllabusRow[6].text, + teachers: syllabusRow[7].text, + className: syllabusRow[8].text, + applyStudentCount: syllabusRow[9].text, + withdrawStudentCount: syllabusRow[10].text, + note: syllabusRow[11].text, + ); return model; } catch (e, stack) { @@ -200,29 +206,24 @@ class CourseConnector { } } - static Future> getCourseSemester(String studentId) async { + static Future?> getCourseSemester(String studentId) async { try { - ConnectorParameter parameter; - Document tagNode; - Element node; - List nodes; - - Map data = { + final ConnectorParameter parameter = ConnectorParameter(_postCourseCNUrl); + parameter.data = { "code": studentId, "format": "-3", }; - parameter = ConnectorParameter(_postCourseCNUrl); - parameter.data = data; - Response response = await Connector.getDataByPostResponse(parameter); - tagNode = parse(response.toString()); - node = tagNode.getElementsByTagName("table")[0]; - nodes = node.getElementsByTagName("tr"); - List semesterJsonList = []; + final Response response = await Connector.getDataByPostResponse(parameter); + final Document tagNode = parse(response.toString()); + + final Element node = tagNode.getElementsByTagName("table")[0]; + final List nodes = node.getElementsByTagName("tr"); + + final List semesterJsonList = []; for (int i = 1; i < nodes.length; i++) { - node = nodes[i]; - String year, semester; - year = node.getElementsByTagName("a")[0].text.split(" ")[0]; - semester = node.getElementsByTagName("a")[0].text.split(" ")[2]; + final Element semesterNode = nodes[i]; + final String year = semesterNode.getElementsByTagName("a")[0].text.split(" ")[0]; + final String semester = semesterNode.getElementsByTagName("a")[0].text.split(" ")[2]; semesterJsonList.add(SemesterJson(year: year, semester: semester)); } return semesterJsonList; @@ -233,7 +234,7 @@ class CourseConnector { } static String strQ2B(String input) { - List newString = []; + final List newString = []; for (int c in input.codeUnits) { if (c == 12288) { c = 32; @@ -247,372 +248,31 @@ class CourseConnector { return String.fromCharCodes(newString); } - static Future getENCourseMainInfoList(String studentId, SemesterJson semester) async { - var info = CourseMainInfo(); - try { - ConnectorParameter parameter; - Document tagNode; - List courseNodes, nodesOne, nodes; - List dayEnum = [Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday, Day.Thursday, Day.Friday, Day.Saturday]; - Map data = { - "code": studentId, - "format": "-2", - "year": semester.year, - "sem": semester.semester, - }; - parameter = ConnectorParameter(_postCourseENUrl); - parameter.data = data; - parameter.charsetName = 'utf-8'; - Response response = await Connector.getDataByPostResponse(parameter); - tagNode = parse(response.toString()); - nodes = tagNode.getElementsByTagName("table"); - courseNodes = nodes[1].getElementsByTagName("tr"); - String studentName; - try { - studentName = strQ2B(nodes[0].getElementsByTagName("td")[4].text).replaceAll(RegExp(r"[\n| ]"), ""); - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - studentName = ""; - } - info.studentName = studentName; - - List courseMainInfoList = []; - for (int i = 1; i < courseNodes.length - 1; i++) { - CourseMainInfoJson courseMainInfo = CourseMainInfoJson(); - CourseMainJson courseMain = CourseMainJson(); - nodesOne = courseNodes[i].getElementsByTagName("td"); - if (nodesOne[16].text.contains("Withdraw")) { - continue; - } - //取得課號 - courseMain.id = strQ2B(nodesOne[0].text).replaceAll(RegExp(r"[\n| ]"), ""); - //取的課程名稱/課程連結 - nodes = nodesOne[1].getElementsByTagName("a"); //確定是否有連結 - if (nodes.isNotEmpty) { - courseMain.name = nodes[0].text; - } else { - courseMain.name = nodesOne[1].text; - } - courseMain.credits = nodesOne[2].text.replaceAll("\n", ""); //學分 - courseMain.hours = nodesOne[3].text.replaceAll("\n", ""); //時數 - - //時間 - for (int j = 0; j < 7; j++) { - Day day = dayEnum[j]; //要做變換網站是從星期日開始 - String time = nodesOne[j + 6].text; - time = strQ2B(time); - courseMain.time[day] = time; - } - - courseMainInfo.course = courseMain; - - int length; - //取得老師名稱 - length = nodesOne[4].innerHtml.split("
").length; - for (String name in nodesOne[4].innerHtml.split("
")) { - TeacherJson teacher = TeacherJson(); - teacher.name = name.replaceAll("\n", ""); - courseMainInfo.teacher.add(teacher); - } - - //取得教室名稱 - length = nodesOne[13].innerHtml.split("
").length; - for (String name in nodesOne[13].innerHtml.split("
").getRange(0, length - 1)) { - ClassroomJson classroom = ClassroomJson(); - classroom.name = name.replaceAll("\n", ""); - courseMainInfo.classroom.add(classroom); - } - - //取得開設教室名稱 - for (Element node in nodesOne[5].getElementsByTagName("a")) { - ClassJson classInfo = ClassJson(); - classInfo.name = node.text; - classInfo.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.openClass.add(classInfo); - } - courseMainInfoList.add(courseMainInfo); - } - info.json = courseMainInfoList; - return info; - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - return null; - } - } - - static Future getTWCourseMainInfoList(String studentId, SemesterJson semester) async { - var info = CourseMainInfo(); - try { - ConnectorParameter parameter; - Document tagNode; - Element node; - List courseNodes, nodesOne, nodes; - List dayEnum = [Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday, Day.Thursday, Day.Friday, Day.Saturday]; - Map data = { - "code": studentId, - "format": "-2", - "year": semester.year, - "sem": semester.semester, - }; - parameter = ConnectorParameter(_postCourseCNUrl); - parameter.data = data; - Response response = await Connector.getDataByPostResponse(parameter); - tagNode = parse(response.toString()); - node = tagNode.getElementsByTagName("table")[1]; - courseNodes = node.getElementsByTagName("tr"); - String studentName; - try { - studentName = RegExp(r"姓名:([\u4E00-\u9FA5]+)").firstMatch(courseNodes[0].text).group(1); - } catch (e) { - studentName = ""; - } - info.studentName = studentName; - List courseMainInfoList = []; - for (int i = 2; i < courseNodes.length - 1; i++) { - CourseMainInfoJson courseMainInfo = CourseMainInfoJson(); - CourseMainJson courseMain = CourseMainJson(); - - nodesOne = courseNodes[i].getElementsByTagName("td"); - if (nodesOne[16].text.contains("撤選")) { - continue; - } - //取得課號 - courseMain.id = strQ2B(nodesOne[0].text).replaceAll(RegExp(r"\s"), ""); - - //取的課程名稱/課程連結 - nodes = nodesOne[1].getElementsByTagName("a"); //確定是否有連結 - if (nodes.isNotEmpty) { - courseMain.name = nodes[0].text; - } else { - courseMain.name = nodesOne[1].text; - } - courseMain.stage = nodesOne[2].text.replaceAll("\n", ""); //階段 - courseMain.credits = nodesOne[3].text.replaceAll("\n", ""); //學分 - courseMain.hours = nodesOne[4].text.replaceAll("\n", ""); //時數 - courseMain.note = nodesOne[19].text.replaceAll("\n", ""); //備註 - if (nodesOne[18].getElementsByTagName("a").isNotEmpty) { - courseMain.scheduleHref = - _courseCNHost + nodesOne[18].getElementsByTagName("a")[0].attributes["href"]; //教學進度大綱 - } - - //時間 - for (int j = 0; j < 7; j++) { - Day day = dayEnum[j]; //要做變換網站是從星期日開始 - String time = nodesOne[j + 8].text; - time = strQ2B(time); - courseMain.time[day] = time; - } - - courseMainInfo.course = courseMain; - - //取得老師名稱 - for (Element node in nodesOne[6].getElementsByTagName("a")) { - TeacherJson teacher = TeacherJson(); - teacher.name = node.text; - teacher.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.teacher.add(teacher); - } - - //取得教室名稱 - for (Element node in nodesOne[15].getElementsByTagName("a")) { - ClassroomJson classroom = ClassroomJson(); - classroom.name = node.text; - classroom.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.classroom.add(classroom); - } - - //取得開設教室名稱 - for (Element node in nodesOne[7].getElementsByTagName("a")) { - ClassJson classInfo = ClassJson(); - classInfo.name = node.text; - classInfo.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.openClass.add(classInfo); - } - - courseMainInfoList.add(courseMainInfo); - } - info.json = courseMainInfoList; - return info; - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - return null; - } - } - - static Future getTWTeacherCourseMainInfoList(String studentId, SemesterJson semester) async { - var info = CourseMainInfo(); - try { - ConnectorParameter parameter; - Document tagNode; - Element node; - List courseNodes, nodesOne, nodes; - List dayEnum = [Day.Sunday, Day.Monday, Day.Tuesday, Day.Wednesday, Day.Thursday, Day.Friday, Day.Saturday]; - Map data = { - "code": studentId, - "format": "-3", - "year": semester.year, - "sem": semester.semester, - }; - parameter = ConnectorParameter(_postTeacherCourseCNUrl); - parameter.data = data; - parameter.charsetName = 'big5'; - Response response = await Connector.getDataByPostResponse(parameter); - tagNode = parse(response.toString()); - node = tagNode.getElementsByTagName("table")[0]; - courseNodes = node.getElementsByTagName("tr"); - String studentName; - try { - studentName = courseNodes[0].text.replaceAll("  ", " ").split(" ")[2]; - } catch (e) { - studentName = ""; - } - info.studentName = studentName; - List courseMainInfoList = []; - for (int i = 2; i < courseNodes.length - 1; i++) { - CourseMainInfoJson courseMainInfo = CourseMainInfoJson(); - CourseMainJson courseMain = CourseMainJson(); - - nodesOne = courseNodes[i].getElementsByTagName("td"); - if (nodesOne[16].text.contains("撤選")) { - continue; - } - //取得課號 - nodes = nodesOne[0].getElementsByTagName("a"); //確定是否有課號 - if (nodes.isNotEmpty) { - courseMain.id = nodes[0].text; - courseMain.href = _courseCNHost + nodes[0].attributes["href"]; - } - //取的課程名稱/課程連結 - nodes = nodesOne[1].getElementsByTagName("a"); //確定是否有連結 - if (nodes.isNotEmpty) { - courseMain.name = nodes[0].text; - } else { - courseMain.name = nodesOne[1].text; - } - courseMain.stage = nodesOne[2].text.replaceAll("\n", ""); //階段 - courseMain.credits = nodesOne[3].text.replaceAll("\n", ""); //學分 - courseMain.hours = nodesOne[4].text.replaceAll("\n", ""); //時數 - courseMain.note = nodesOne[20].text.replaceAll("\n", ""); //備註 - if (nodesOne[19].getElementsByTagName("a").isNotEmpty) { - courseMain.scheduleHref = - _courseCNHost + nodesOne[19].getElementsByTagName("a")[0].attributes["href"]; //教學進度大綱 - } - - //時間 - for (int j = 0; j < 7; j++) { - Day day = dayEnum[j]; //要做變換網站是從星期日開始 - String time = nodesOne[j + 8].text; - time = strQ2B(time); - courseMain.time[day] = time; - } - - courseMainInfo.course = courseMain; - - //取得老師名稱 - TeacherJson teacher = TeacherJson(); - teacher.name = ""; - teacher.href = ""; - courseMainInfo.teacher.add(teacher); - - //取得教室名稱 - for (Element node in nodesOne[15].getElementsByTagName("a")) { - ClassroomJson classroom = ClassroomJson(); - classroom.name = node.text; - classroom.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.classroom.add(classroom); - } - - //取得開設教室名稱 - for (Element node in nodesOne[7].getElementsByTagName("a")) { - ClassJson classInfo = ClassJson(); - classInfo.name = node.text; - classInfo.href = _courseCNHost + node.attributes["href"]; - courseMainInfo.openClass.add(classInfo); - } - - courseMainInfoList.add(courseMainInfo); - } - info.json = courseMainInfoList; - return info; - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - return null; - } - } - static Future getGraduation(String year, String department) async { - ConnectorParameter parameter; - String result; - Document tagNode; - Element node; - List nodes; - RegExp exp; - RegExpMatch matches; - Map graduationMap = {}; - try { - parameter = ConnectorParameter("https://aps.ntut.edu.tw/course/tw/Cprog.jsp"); - parameter.data = {"format": "-3", "year": year, "matric": "7"}; - result = await Connector.getDataByGet(parameter); - tagNode = parse(result); - node = tagNode.getElementsByTagName("tbody").first; - nodes = node.getElementsByTagName("tr"); - String href; - for (int i = 1; i < nodes.length; i++) { - node = nodes[i]; - node = node.getElementsByTagName("a").first; - if (node.text.contains(department)) { - href = node.attributes["href"]; - break; - } - } - href = "https://aps.ntut.edu.tw/course/tw/$href"; - parameter = ConnectorParameter(href); - result = await Connector.getDataByGet(parameter); - - exp = RegExp(r"最低畢業學分:?(\d+)學分"); - matches = exp.firstMatch(result); - graduationMap["lowCredit"] = int.parse(matches.group(1)); - - exp = RegExp(r"共同必修:?(\d+)學分"); - matches = exp.firstMatch(result); - graduationMap["△"] = int.parse(matches.group(1)); - - exp = RegExp(r"專業必修:?(\d+)學分"); - matches = exp.firstMatch(result); - graduationMap["▲"] = int.parse(matches.group(1)); - - exp = RegExp(r"專業選修:?(\d+)學分"); - matches = exp.firstMatch(result); - graduationMap["★"] = int.parse(matches.group(1)); - - /* - exp = RegExp("通識博雅課程應修滿(\d+)學分"); - matches = exp.firstMatch(result); - exp = RegExp("跨系所專業選修(\d+)學分為畢業學分"); - matches = exp.firstMatch(result); - */ - return graduationMap; - } catch (e, stack) { - Log.eWithStack(e.toString(), stack); - return null; - } + final ConnectorParameter parameter = ConnectorParameter("https://aps.ntut.edu.tw/course/tw/Cprog.jsp"); + parameter.data = {"format": "-3", "year": year, "matric": "7"}; + final String result = await Connector.getDataByGet(parameter); + final Document tagNode = parse(result); + + final Element node = tagNode.getElementsByTagName("tbody").first; + final List nodes = node.getElementsByTagName("tr"); + final String redirectHypertextRef = + nodes.firstWhere((node) => node.text.contains(department)).getElementsByTagName("a").first.attributes["href"]!; + + final Map graduationMap = await _getGraduationCreditMap(redirectHypertextRef); + return graduationMap; } - static Future> getYearList() async { - ConnectorParameter parameter; - String result; - Document tagNode; - Element node; - List nodes; - List resultList = []; + static Future?> getYearList() async { try { - parameter = ConnectorParameter("https://aps.ntut.edu.tw/course/tw/Cprog.jsp"); + final ConnectorParameter parameter = ConnectorParameter("https://aps.ntut.edu.tw/course/tw/Cprog.jsp"); parameter.data = {"format": "-1"}; - result = await Connector.getDataByPost(parameter); - tagNode = parse(result); - nodes = tagNode.getElementsByTagName("a"); + final String result = await Connector.getDataByPost(parameter); + final Document tagNode = parse(result); + final List nodes = tagNode.getElementsByTagName("a"); + final List resultList = []; for (int i = 0; i < nodes.length; i++) { - node = nodes[i]; + final Element node = nodes[i]; resultList.add(node.text); } return resultList; @@ -627,22 +287,22 @@ class CourseConnector { name 名稱 code 參數 */ - static Future> getDivisionList(String year) async { - ConnectorParameter parameter; - String result; - Document tagNode; - Element node; - List nodes; - List resultList = []; + static Future?> getDivisionList(String year) async { try { - parameter = ConnectorParameter(_creditUrl); + final ConnectorParameter parameter = ConnectorParameter(_creditUrl); parameter.data = {"format": "-2", "year": year}; - result = await Connector.getDataByPost(parameter); - tagNode = parse(result); - nodes = tagNode.getElementsByTagName("a"); + final String result = await Connector.getDataByPost(parameter); + final Document tagNode = parse(result); + final List nodes = tagNode.getElementsByTagName("a"); + final List resultList = []; + for (int i = 0; i < nodes.length; i++) { - node = nodes[i]; - Map code = Uri.parse(node.attributes["href"]).queryParameters; + final Element node = nodes[i]; + final href = node.attributes["href"]; + if (href == null || href.isEmpty) { + throw Exception("getDivisionList: href is null or empty."); + } + final Map code = Uri.parse(href).queryParameters; resultList.add({"name": node.text, "code": code}); } return resultList; @@ -657,24 +317,21 @@ class CourseConnector { name 名稱 code 參數 */ - static Future> getDepartmentList(Map code) async { - ConnectorParameter parameter; - String result; - Document tagNode; - Element node; - List nodes; - List resultList = []; + static Future>?> getDepartmentList(Map code) async { try { - parameter = ConnectorParameter(_creditUrl); - parameter.data = code; - result = await Connector.getDataByPost(parameter); - tagNode = parse(result); - node = tagNode.getElementsByTagName("table").first; - nodes = node.getElementsByTagName("a"); + final ConnectorParameter parameter = ConnectorParameter(_creditUrl); + final String result = await Connector.getDataByPost(parameter); + final Document tagNode = parse(result); + final List resultList = []; + final List nodes = tagNode.getElementsByTagName("table").first.getElementsByTagName("a"); for (int i = 0; i < nodes.length; i++) { - node = nodes[i]; - Map code = Uri.parse(node.attributes["href"]).queryParameters; - String name = node.text.replaceAll(RegExp("[ |s]"), ""); + final Element node = nodes[i]; + final href = node.attributes["href"]; + if (href == null || href.isEmpty) { + throw Exception("getDepartmentList: href is null or empty."); + } + final Map code = Uri.parse(href).queryParameters; + final String name = node.text.replaceAll(RegExp("[ |s]"), ""); resultList.add({"name": name, "code": code}); } return resultList; @@ -688,7 +345,7 @@ class CourseConnector { Map Key minGraduationCredits */ - static Future getCreditInfo(Map code, String select) async { + static Future getCreditInfo(Map code, String select) async { ConnectorParameter parameter; String result; Document tagNode; @@ -764,4 +421,78 @@ class CourseConnector { return null; } } + + static Future _getSSORedirectNodesInLoginPhase() async { + final ConnectorParameter parameter = ConnectorParameter(_ssoLoginUrl); + final Map data = { + "apUrl": "https://aps.ntut.edu.tw/course/tw/courseSID.jsp", + "apOu": "aa_0010-", + "sso": "true", + "datetime1": DateTime.now().millisecondsSinceEpoch.toString() + }; + parameter.data = data; + final String result = await Connector.getDataByGet(parameter); + + final Document tagNode = parse(result); + return tagNode; + } + + static Map _getSSOLoginPayload(Document ssoRedirectTagNode) { + final Map data = {}; + final List nodes = ssoRedirectTagNode.getElementsByTagName("input"); + + for (Element node in nodes) { + final String? name = node.attributes['name']; + final String? value = node.attributes['value']; + if (name == null || value == null) { + throw Exception("Cannot fetch name or value."); + } + data[name] = value; + } + + return data; + } + + static String _getSSOLoginJumpUrl(Document ssoRedirectTagNode) { + final String? jumpUrl = ssoRedirectTagNode.getElementsByTagName("form")[0].attributes["action"]; + + if (jumpUrl == null) { + throw Exception("Cannot fetch jumpUrl."); + } + + return jumpUrl; + } + + static Future _tryToSSOLoginOrThrowException(String jumpUrl, Map payload) async { + final ConnectorParameter parameter = ConnectorParameter(jumpUrl); + parameter.data = payload; + await Connector.getDataByPostResponse(parameter); + } + + static Future> _getGraduationCreditMap(String href) async { + final ConnectorParameter parameter = ConnectorParameter(href); + final String result = await Connector.getDataByGet(parameter); + final Map graduationMap = {}; + + graduationMap["lowCredit"] = _getGraduationCredit(result, RegExp(r"最低畢業學分:?(\d+)學分")); + graduationMap["△"] = _getGraduationCredit(result, RegExp(r"共同必修:?(\d+)學分")); + graduationMap["▲"] = _getGraduationCredit(result, RegExp(r"專業必修:?(\d+)學分")); + graduationMap["★"] = _getGraduationCredit(result, RegExp(r"專業選修:?(\d+)學分")); + + return graduationMap; + } + + static int _getGraduationCredit(String content, RegExp exp) { + final RegExpMatch? matches = exp.firstMatch(content); + if (matches == null) { + return 0; + } + + final String? creditText = matches.group(1); + if (creditText == null) { + return 0; + } + + return int.parse(creditText); + } } diff --git a/lib/src/connector/ischool_plus_connector.dart b/lib/src/connector/ischool_plus_connector.dart index 4086ccb1..10ae2531 100644 --- a/lib/src/connector/ischool_plus_connector.dart +++ b/lib/src/connector/ischool_plus_connector.dart @@ -112,7 +112,7 @@ class ISchoolPlusConnector { } } - static Future>> getCourseFile(String courseId) async { + static Future>> getCourseFile(int courseId) async { ConnectorParameter parameter; String result; html.Document tagNode; @@ -263,7 +263,7 @@ class ISchoolPlusConnector { static String bid; - static Future>> getCourseAnnouncement(String courseId) async { + static Future>> getCourseAnnouncement(int courseId) async { String result; var value = ReturnWithStatus>(); try { @@ -460,7 +460,7 @@ class ISchoolPlusConnector { String result; try { parameter = ConnectorParameter("https://istudy.ntut.edu.tw/forum/subscribe.php"); - parameter.data = {"bid": bid}; + parameter.data = {"bid": bid.toString()}; await Connector.getDataByPost(parameter); result = await Connector.getDataByPost(parameter); tagNode = html.parse(result); @@ -471,7 +471,7 @@ class ISchoolPlusConnector { } } - static Future getBid(String courseId) async { + static Future getBid() async { /* ConnectorParameter parameter; html.Document tagNode; @@ -492,7 +492,7 @@ class ISchoolPlusConnector { return bid; } - static Future _selectCourse(String courseId) async { + static Future _selectCourse(int courseId) async { ConnectorParameter parameter; html.Document tagNode; html.Element node; @@ -508,7 +508,7 @@ class ISchoolPlusConnector { for (int i = 1; i < nodes.length; i++) { node = nodes[i]; String name = node.text.split("_").last; - if (name == courseId) { + if (name == courseId.toString()) { courseValue = node.attributes["value"]; break; } diff --git a/lib/src/connector/score_connector.dart b/lib/src/connector/score_connector.dart index 9e0c08d1..9c6f338a 100644 --- a/lib/src/connector/score_connector.dart +++ b/lib/src/connector/score_connector.dart @@ -2,7 +2,7 @@ // @dart=2.10 import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/connector/ntut_connector.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; +import 'package:flutter_app/src/model/course/course_semester.dart'; import 'package:flutter_app/src/model/course/course_score_json.dart'; import 'package:html/dom.dart'; import 'package:html/parser.dart'; @@ -97,7 +97,7 @@ class ScoreConnector { SemesterCourseScoreJson courseScore = SemesterCourseScoreJson(); - SemesterJson semester = SemesterJson(); + SemesterJson semester = SemesterJson.origin(); semester.year = h3Node.text.split(" ")[0]; semester.semester = h3Node.text.split(" ")[3]; courseScore.semester = semester; @@ -152,7 +152,7 @@ class ScoreConnector { .where((row) => row.getElementsByTagName("td").length >= 7) .toList(growable: false); for (int i = 0; i < (rankNodes.length / 3).floor(); i++) { - SemesterJson semester = SemesterJson(); + SemesterJson semester = SemesterJson.origin(); String semesterString = rankNodes[i * 3 + 2].getElementsByTagName("td")[0].innerHtml.split("
").first; semester.year = semesterString.split(" ")[0]; semester.semester = semesterString.split(" ").reversed.toList()[0]; diff --git a/lib/src/model/course/course_class_json.dart b/lib/src/model/course/course_class_json.dart deleted file mode 100644 index c3d36435..00000000 --- a/lib/src/model/course/course_class_json.dart +++ /dev/null @@ -1,254 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; -import 'package:flutter_app/src/model/json_init.dart'; -import 'package:flutter_app/src/util/language_util.dart'; -import 'package:json_annotation/json_annotation.dart'; -import 'package:sprintf/sprintf.dart'; - -part 'course_class_json.g.dart'; - -@JsonSerializable() -class CourseMainJson { - String name; - String id; - String href; - String note; //備註 - String stage; //階段 - String credits; //學分 - String hours; //時數 - String scheduleHref; // 教學進度大綱 - Map time; //時間 - - CourseMainJson( - {this.name, this.href, this.id, this.credits, this.hours, this.stage, this.note, this.time, this.scheduleHref}) { - name = JsonInit.stringInit(name); - id = JsonInit.stringInit(id); - href = JsonInit.stringInit(href); - note = JsonInit.stringInit(note); - stage = JsonInit.stringInit(stage); - credits = JsonInit.stringInit(credits); - hours = JsonInit.stringInit(hours); - scheduleHref = JsonInit.stringInit(scheduleHref); - time = time ?? {}; - } - - bool get isEmpty { - return name.isEmpty && - href.isEmpty && - note.isEmpty && - stage.isEmpty && - credits.isEmpty && - hours.isEmpty && - scheduleHref.isEmpty; - } - - @override - String toString() { - return sprintf( - "name :%s \nid :%s \nhref :%s \nstage :%s \ncredits :%s \nhours :%s \nscheduleHref :%s \nnote :%s \n", - [name, id, href, stage, credits, hours, scheduleHref, note]); - } - - factory CourseMainJson.fromJson(Map json) => _$CourseMainJsonFromJson(json); - - Map toJson() => _$CourseMainJsonToJson(this); -} - -@JsonSerializable() -class CourseExtraJson { - String id; - String name; - String href; //課程名稱用於取得英文 - String category; //類別 (必修...) - String selectNumber; //選課人數 - String withdrawNumber; //徹選人數 - String openClass; //開課班級(計算學分用) - - CourseExtraJson({this.name, this.category, this.selectNumber, this.withdrawNumber, this.href}) { - id = JsonInit.stringInit(id); - name = JsonInit.stringInit(name); - href = JsonInit.stringInit(href); - category = JsonInit.stringInit(category); - selectNumber = JsonInit.stringInit(selectNumber); - withdrawNumber = JsonInit.stringInit(withdrawNumber); - openClass = JsonInit.stringInit(openClass); - } - - bool get isEmpty { - return id.isEmpty && - name.isEmpty && - category.isEmpty && - selectNumber.isEmpty && - withdrawNumber.isEmpty && - openClass.isEmpty; - } - - @override - String toString() { - return sprintf( - "id :%s \nname :%s \ncategory :%s \nselectNumber :%s \nwithdrawNumber :%s \nopenClass :%s \n", - [id, name, category, selectNumber, withdrawNumber, openClass]); - } - - factory CourseExtraJson.fromJson(Map json) => _$CourseExtraJsonFromJson(json); - - Map toJson() => _$CourseExtraJsonToJson(this); -} - -@JsonSerializable() -class ClassJson { - String name; - String href; - - ClassJson({this.name, this.href}) { - name = JsonInit.stringInit(name); - href = JsonInit.stringInit(href); - } - - bool get isEmpty { - return name.isEmpty && href.isEmpty; - } - - @override - String toString() { - return sprintf("name : %s \n" "href : %s \n", [name, href]); - } - - factory ClassJson.fromJson(Map json) => _$ClassJsonFromJson(json); - - Map toJson() => _$ClassJsonToJson(this); -} - -@JsonSerializable() -class ClassroomJson { - String name; - String href; - bool mainUse; - - ClassroomJson({this.name, this.href, this.mainUse}) { - name = JsonInit.stringInit(name); - href = JsonInit.stringInit(href); - mainUse = mainUse ?? false; - } - - bool get isEmpty { - return name.isEmpty && href.isEmpty; - } - - @override - String toString() { - return sprintf("name : %s \nhref : %s \nmainUse : %s \n", [name, href, mainUse.toString()]); - } - - factory ClassroomJson.fromJson(Map json) => _$ClassroomJsonFromJson(json); - - Map toJson() => _$ClassroomJsonToJson(this); -} - -@JsonSerializable() -class TeacherJson { - String name; - String href; - - TeacherJson({this.name, this.href}) { - name = JsonInit.stringInit(name); - href = JsonInit.stringInit(href); - } - - bool get isEmpty { - return name.isEmpty && href.isEmpty; - } - - @override - String toString() { - return sprintf("name : %s \n" "href : %s \n", [name, href]); - } - - factory TeacherJson.fromJson(Map json) => _$TeacherJsonFromJson(json); - - Map toJson() => _$TeacherJsonToJson(this); -} - -@JsonSerializable() -class SemesterJson { - String year; - String semester; - - SemesterJson({this.year, this.semester}) { - year = JsonInit.stringInit(year); - semester = JsonInit.stringInit(semester); - } - - factory SemesterJson.fromJson(Map json) => _$SemesterJsonFromJson(json); - - Map toJson() => _$SemesterJsonToJson(this); - - bool get isEmpty { - return year.isEmpty && semester.isEmpty; - } - - @override - String toString() { - return sprintf("year : %s \n" "semester : %s \n", [year, semester]); - } - - @override - bool operator ==(dynamic other) { - if (other is! SemesterJson) { - return false; - } - - final isSemesterSame = int.tryParse(other.semester) == int.tryParse(semester); - final isYearSame = int.tryParse(other.year) == int.tryParse(year); - - return isSemesterSame && isYearSame; - } - - @override - int get hashCode => Object.hashAll([semester.hashCode, year.hashCode]); -} - -@JsonSerializable() -class ClassmateJson { - String className; //電子一甲 - String studentEnglishName; - String studentName; - String studentId; - String href; - bool isSelect; //是否撤選 - - ClassmateJson({this.className, this.studentEnglishName, this.studentName, this.studentId, this.isSelect, this.href}) { - className = JsonInit.stringInit(className); - studentEnglishName = JsonInit.stringInit(studentEnglishName); - studentName = JsonInit.stringInit(studentName); - studentId = JsonInit.stringInit(studentId); - href = JsonInit.stringInit(href); - isSelect = isSelect ?? false; - } - - bool get isEmpty { - return className.isEmpty && studentEnglishName.isEmpty && studentName.isEmpty && studentId.isEmpty && href.isEmpty; - } - - @override - String toString() { - return sprintf( - "className : %s \nstudentEnglishName : %s \nstudentName : %s \nstudentId : %s \nhref : %s \nisSelect : %s \n", - [className, studentEnglishName, studentName, studentId, href, isSelect.toString()]); - } - - String getName() { - String name; - if (LanguageUtil.getLangIndex() == LangEnum.en) { - name = studentEnglishName; - } - name = name ?? studentName; - name = (name.contains(RegExp(r"\w"))) ? name : studentName; - return name; - } - - factory ClassmateJson.fromJson(Map json) => _$ClassmateJsonFromJson(json); - - Map toJson() => _$ClassmateJsonToJson(this); -} diff --git a/lib/src/model/course/course_class_json.g.dart b/lib/src/model/course/course_class_json.g.dart deleted file mode 100644 index 8536047d..00000000 --- a/lib/src/model/course/course_class_json.g.dart +++ /dev/null @@ -1,170 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'course_class_json.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -CourseMainJson _$CourseMainJsonFromJson(Map json) { - return CourseMainJson( - name: json['name'] as String, - href: json['href'] as String, - id: json['id'] as String, - credits: json['credits'] as String, - hours: json['hours'] as String, - stage: json['stage'] as String, - note: json['note'] as String, - time: (json['time'] as Map)?.map( - (k, e) => MapEntry(_$enumDecodeNullable(_$DayEnumMap, k), e as String), - ), - scheduleHref: json['scheduleHref'] as String, - ); -} - -Map _$CourseMainJsonToJson(CourseMainJson instance) => { - 'name': instance.name, - 'id': instance.id, - 'href': instance.href, - 'note': instance.note, - 'stage': instance.stage, - 'credits': instance.credits, - 'hours': instance.hours, - 'scheduleHref': instance.scheduleHref, - 'time': instance.time?.map((k, e) => MapEntry(_$DayEnumMap[k], e)), - }; - -T _$enumDecode( - Map enumValues, - dynamic source, { - T unknownValue, -}) { - if (source == null) { - throw ArgumentError('A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}'); - } - - final value = enumValues.entries.singleWhere((e) => e.value == source, orElse: () => null)?.key; - - if (value == null && unknownValue == null) { - throw ArgumentError('`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}'); - } - return value ?? unknownValue; -} - -T _$enumDecodeNullable( - Map enumValues, - dynamic source, { - T unknownValue, -}) { - if (source == null) { - return null; - } - return _$enumDecode(enumValues, source, unknownValue: unknownValue); -} - -const _$DayEnumMap = { - Day.Monday: 'Monday', - Day.Tuesday: 'Tuesday', - Day.Wednesday: 'Wednesday', - Day.Thursday: 'Thursday', - Day.Friday: 'Friday', - Day.Saturday: 'Saturday', - Day.Sunday: 'Sunday', - Day.UnKnown: 'UnKnown', -}; - -CourseExtraJson _$CourseExtraJsonFromJson(Map json) { - return CourseExtraJson( - name: json['name'] as String, - category: json['category'] as String, - selectNumber: json['selectNumber'] as String, - withdrawNumber: json['withdrawNumber'] as String, - href: json['href'] as String, - ) - ..id = json['id'] as String - ..openClass = json['openClass'] as String; -} - -Map _$CourseExtraJsonToJson(CourseExtraJson instance) => { - 'id': instance.id, - 'name': instance.name, - 'href': instance.href, - 'category': instance.category, - 'selectNumber': instance.selectNumber, - 'withdrawNumber': instance.withdrawNumber, - 'openClass': instance.openClass, - }; - -ClassJson _$ClassJsonFromJson(Map json) { - return ClassJson( - name: json['name'] as String, - href: json['href'] as String, - ); -} - -Map _$ClassJsonToJson(ClassJson instance) => { - 'name': instance.name, - 'href': instance.href, - }; - -ClassroomJson _$ClassroomJsonFromJson(Map json) { - return ClassroomJson( - name: json['name'] as String, - href: json['href'] as String, - mainUse: json['mainUse'] as bool, - ); -} - -Map _$ClassroomJsonToJson(ClassroomJson instance) => { - 'name': instance.name, - 'href': instance.href, - 'mainUse': instance.mainUse, - }; - -TeacherJson _$TeacherJsonFromJson(Map json) { - return TeacherJson( - name: json['name'] as String, - href: json['href'] as String, - ); -} - -Map _$TeacherJsonToJson(TeacherJson instance) => { - 'name': instance.name, - 'href': instance.href, - }; - -SemesterJson _$SemesterJsonFromJson(Map json) { - return SemesterJson( - year: json['year'] as String, - semester: json['semester'] as String, - ); -} - -Map _$SemesterJsonToJson(SemesterJson instance) => { - 'year': instance.year, - 'semester': instance.semester, - }; - -ClassmateJson _$ClassmateJsonFromJson(Map json) { - return ClassmateJson( - className: json['className'] as String, - studentEnglishName: json['studentEnglishName'] as String, - studentName: json['studentName'] as String, - studentId: json['studentId'] as String, - isSelect: json['isSelect'] as bool, - href: json['href'] as String, - ); -} - -Map _$ClassmateJsonToJson(ClassmateJson instance) => { - 'className': instance.className, - 'studentEnglishName': instance.studentEnglishName, - 'studentName': instance.studentName, - 'studentId': instance.studentId, - 'href': instance.href, - 'isSelect': instance.isSelect, - }; diff --git a/lib/src/model/course/course_main_extra_json.dart b/lib/src/model/course/course_main_extra_json.dart deleted file mode 100644 index b2782736..00000000 --- a/lib/src/model/course/course_main_extra_json.dart +++ /dev/null @@ -1,105 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:json_annotation/json_annotation.dart'; -import 'package:sprintf/sprintf.dart'; - -part 'course_main_extra_json.g.dart'; - -@JsonSerializable() -class CourseExtraInfoJson { - //點入課程使用 - SemesterJson courseSemester; - CourseExtraJson course; - List classmate; //修課同學 - - CourseExtraInfoJson({this.courseSemester, this.course, this.classmate}) { - classmate = classmate ?? []; - courseSemester = courseSemester ?? SemesterJson(); - course = course ?? CourseExtraJson(); - } - - bool get isEmpty { - return classmate.isEmpty && courseSemester.isEmpty && course.isEmpty; - } - - @override - String toString() { - return sprintf( - "---------courseSemester-------- \n%s \n---------course-------- \n%s \n---------classmateList-------- \n%s \n", - [courseSemester.toString(), course.toString(), classmate.toString()]); - } - - factory CourseExtraInfoJson.fromJson(Map json) => _$CourseExtraInfoJsonFromJson(json); - - Map toJson() => _$CourseExtraInfoJsonToJson(this); -} - -@JsonSerializable() -class CourseMainInfoJson { - CourseMainJson course; - List teacher; //開課老師 - List classroom; //使用教室 - List openClass; //開課班級 - CourseMainInfoJson({this.course, this.teacher, this.classroom, this.openClass}) { - course = course ?? CourseMainJson(); - teacher = teacher ?? []; - classroom = classroom ?? []; - openClass = openClass ?? []; - } - - String getOpenClassName() { - String name = ""; - for (ClassJson value in openClass) { - name += '${value.name} '; - } - return name; - } - - String getTeacherName() { - String name = ""; - for (TeacherJson value in teacher) { - name += '${value.name} '; - } - return name; - } - - String getClassroomName() { - String name = ""; - for (ClassroomJson value in classroom) { - name += '${value.name} '; - } - return name; - } - - List getClassroomNameList() { - List name = []; - for (ClassroomJson value in classroom) { - name.add(value.name); - } - return name; - } - - List getClassroomHrefList() { - List href = []; - for (ClassroomJson value in classroom) { - href.add(value.href); - } - return href; - } - - bool get isEmpty { - return course.isEmpty && teacher.isEmpty && classroom.isEmpty && openClass.isEmpty; - } - - @override - String toString() { - return sprintf( - "---------course-------- \n%s \n---------teacherList-------- \n%s \n---------classroomList-------- \n%s \n---------openClassList-------- \n%s \n", - [course.toString(), teacher.toString(), classroom.toString(), openClass.toString()]); - } - - factory CourseMainInfoJson.fromJson(Map json) => _$CourseMainInfoJsonFromJson(json); - - Map toJson() => _$CourseMainInfoJsonToJson(this); -} diff --git a/lib/src/model/course/course_main_extra_json.g.dart b/lib/src/model/course/course_main_extra_json.g.dart deleted file mode 100644 index ca62cafd..00000000 --- a/lib/src/model/course/course_main_extra_json.g.dart +++ /dev/null @@ -1,48 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'course_main_extra_json.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -CourseExtraInfoJson _$CourseExtraInfoJsonFromJson(Map json) { - return CourseExtraInfoJson( - courseSemester: - json['courseSemester'] == null ? null : SemesterJson.fromJson(json['courseSemester'] as Map), - course: json['course'] == null ? null : CourseExtraJson.fromJson(json['course'] as Map), - classmate: (json['classmate'] as List) - ?.map((e) => e == null ? null : ClassmateJson.fromJson(e as Map)) - ?.toList(), - ); -} - -Map _$CourseExtraInfoJsonToJson(CourseExtraInfoJson instance) => { - 'courseSemester': instance.courseSemester, - 'course': instance.course, - 'classmate': instance.classmate, - }; - -CourseMainInfoJson _$CourseMainInfoJsonFromJson(Map json) { - return CourseMainInfoJson( - course: json['course'] == null ? null : CourseMainJson.fromJson(json['course'] as Map), - teacher: (json['teacher'] as List) - ?.map((e) => e == null ? null : TeacherJson.fromJson(e as Map)) - ?.toList(), - classroom: (json['classroom'] as List) - ?.map((e) => e == null ? null : ClassroomJson.fromJson(e as Map)) - ?.toList(), - openClass: (json['openClass'] as List) - ?.map((e) => e == null ? null : ClassJson.fromJson(e as Map)) - ?.toList(), - ); -} - -Map _$CourseMainInfoJsonToJson(CourseMainInfoJson instance) => { - 'course': instance.course, - 'teacher': instance.teacher, - 'classroom': instance.classroom, - 'openClass': instance.openClass, - }; diff --git a/lib/src/model/course/course_score_json.dart b/lib/src/model/course/course_score_json.dart index d7aeac04..ec8081d5 100644 --- a/lib/src/model/course/course_score_json.dart +++ b/lib/src/model/course/course_score_json.dart @@ -1,6 +1,6 @@ // TODO: remove sdk version selector after migrating to null-safety. // @dart=2.10 -import 'package:flutter_app/src/model/course/course_class_json.dart'; +import 'package:flutter_app/src/model/course/course_semester.dart'; import 'package:flutter_app/src/model/json_init.dart'; import 'package:flutter_app/src/util/language_util.dart'; import 'package:json_annotation/json_annotation.dart'; @@ -248,7 +248,7 @@ class SemesterCourseScoreJson { now = now ?? RankJson(); history = history ?? RankJson(); courseScoreList = courseScoreList ?? []; - semester = semester ?? SemesterJson(); + semester = semester ?? SemesterJson.origin(); averageScore = averageScore ?? 0; performanceScore = performanceScore ?? 0; totalCredit = totalCredit ?? 0; diff --git a/lib/src/model/course/course_semester.dart b/lib/src/model/course/course_semester.dart new file mode 100644 index 00000000..df33d2ff --- /dev/null +++ b/lib/src/model/course/course_semester.dart @@ -0,0 +1,37 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter_app/src/model/json_init.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:sprintf/sprintf.dart'; + +part 'course_semester.g.dart'; + +@JsonSerializable() +class SemesterJson with EquatableMixin { + String year; + String semester; + + SemesterJson.origin() + : year = "0", + semester = "0"; + + SemesterJson({required this.year, required this.semester}) { + year = JsonInit.stringInit(year); + semester = JsonInit.stringInit(semester); + } + + factory SemesterJson.fromJson(Map json) => _$SemesterJsonFromJson(json); + + Map toJson() => _$SemesterJsonToJson(this); + + bool get isEmpty { + return year.isEmpty && semester.isEmpty; + } + + @override + String toString() { + return sprintf("year : %s \n" "semester : %s \n", [year, semester]); + } + + @override + List get props => [year, semester]; +} diff --git a/lib/src/model/course/course_semester.g.dart b/lib/src/model/course/course_semester.g.dart new file mode 100644 index 00000000..b09ab650 --- /dev/null +++ b/lib/src/model/course/course_semester.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course_semester.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SemesterJson _$SemesterJsonFromJson(Map json) => SemesterJson( + year: json['year'] as String, + semester: json['semester'] as String, + ); + +Map _$SemesterJsonToJson(SemesterJson instance) => { + 'year': instance.year, + 'semester': instance.semester, + }; diff --git a/lib/src/model/course/course_syllabus_json.dart b/lib/src/model/course/course_syllabus.dart similarity index 100% rename from lib/src/model/course/course_syllabus_json.dart rename to lib/src/model/course/course_syllabus.dart diff --git a/lib/src/model/coursetable/course.dart b/lib/src/model/coursetable/course.dart new file mode 100644 index 00000000..848779c0 --- /dev/null +++ b/lib/src/model/coursetable/course.dart @@ -0,0 +1,91 @@ +import 'package:flutter_app/src/model/coursetable/course_period.dart'; +import 'package:json_annotation/json_annotation.dart'; + +import '../json_init.dart'; + +part 'course.g.dart'; + +@JsonSerializable() +class Course { + late final int id; + final String name; + late final double stage; + late final double credit; + late final int periodCount; + final String category; + late final List teachers; + late final List classNames; + late final List coursePeriods; + late final List classrooms; + final String applyStatus; + final String language; + final String syllabusLink; + final String note; + + Course( + {required this.id, + required this.name, + required this.stage, + required this.credit, + required this.periodCount, + required this.category, + required this.teachers, + required this.classNames, + required this.coursePeriods, + required this.classrooms, + required this.applyStatus, + required this.language, + required this.syllabusLink, + required this.note}); + + Course.parseNodeString({ + required String idString, + required this.name, + required String stageString, + required String creditString, + required String periodCountString, + required this.category, + required String teacherString, + required String classNameString, + required List periodSlots, + required String classroomString, + required this.applyStatus, + required this.language, + required this.syllabusLink, + required this.note, + }) { + id = JsonInit.intInit(idString); + stage = JsonInit.doubleInit(stageString); + credit = JsonInit.doubleInit(creditString); + periodCount = JsonInit.intInit(periodCountString); + teachers = teacherString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); + coursePeriods = _convertPeriodSlotsToCoursePeriods(periodSlots); + classrooms = classroomString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); + classNames = classNameString.split(RegExp(r"\n")).map((element) => element.trim()).toList(); + } + + bool isEmpty() => id == 0; + + List _convertPeriodSlotsToCoursePeriods(List periodSlots) { + List coursePeriods = []; + for (int weekday = 1; weekday <= 7; weekday++) { + String weekdaySlot = periodSlots[weekday % 7]; + if (_isNullOrEmpty(weekdaySlot)) { + continue; + } + List periods = weekdaySlot.split(RegExp(r"\s")); + for (String period in periods) { + coursePeriods.add(CoursePeriod(weekday: weekday, period: period)); + } + } + return coursePeriods; + } + + bool _isNullOrEmpty(String? text) { + return text == null || text.replaceAll(RegExp(r"\s"), "").isEmpty; + } + + factory Course.fromJson(Map json) => _$CourseFromJson(json); + + Map toJson() => _$CourseToJson(this); +} diff --git a/lib/src/model/coursetable/course.g.dart b/lib/src/model/coursetable/course.g.dart new file mode 100644 index 00000000..b97e54e2 --- /dev/null +++ b/lib/src/model/coursetable/course.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Course _$CourseFromJson(Map json) => Course( + id: json['id'] as int, + name: json['name'] as String, + stage: (json['stage'] as num).toDouble(), + credit: (json['credit'] as num).toDouble(), + periodCount: json['periodCount'] as int, + category: json['category'] as String, + teachers: (json['teachers'] as List).map((e) => e as String).toList(), + classNames: (json['classNames'] as List).map((e) => e as String).toList(), + coursePeriods: (json['coursePeriods'] as List) + .map((e) => CoursePeriod.fromJson(e as Map)) + .toList(), + classrooms: (json['classrooms'] as List).map((e) => e as String).toList(), + applyStatus: json['applyStatus'] as String, + language: json['language'] as String, + syllabusLink: json['syllabusLink'] as String, + note: json['note'] as String, + ); + +Map _$CourseToJson(Course instance) => { + 'id': instance.id, + 'name': instance.name, + 'stage': instance.stage, + 'credit': instance.credit, + 'periodCount': instance.periodCount, + 'category': instance.category, + 'teachers': instance.teachers, + 'classNames': instance.classNames, + 'coursePeriods': instance.coursePeriods, + 'classrooms': instance.classrooms, + 'applyStatus': instance.applyStatus, + 'language': instance.language, + 'syllabusLink': instance.syllabusLink, + 'note': instance.note, + }; diff --git a/lib/src/model/coursetable/course_period.dart b/lib/src/model/coursetable/course_period.dart new file mode 100644 index 00000000..198fd374 --- /dev/null +++ b/lib/src/model/coursetable/course_period.dart @@ -0,0 +1,15 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'course_period.g.dart'; + +@JsonSerializable() +class CoursePeriod { + final int weekday; + final String period; + + CoursePeriod({required this.weekday, required this.period}); + + factory CoursePeriod.fromJson(Map json) => _$CoursePeriodFromJson(json); + + Map toJson() => _$CoursePeriodToJson(this); +} diff --git a/lib/src/model/coursetable/course_period.g.dart b/lib/src/model/coursetable/course_period.g.dart new file mode 100644 index 00000000..b27a009a --- /dev/null +++ b/lib/src/model/coursetable/course_period.g.dart @@ -0,0 +1,17 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course_period.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CoursePeriod _$CoursePeriodFromJson(Map json) => CoursePeriod( + weekday: json['weekday'] as int, + period: json['period'] as String, + ); + +Map _$CoursePeriodToJson(CoursePeriod instance) => { + 'weekday': instance.weekday, + 'period': instance.period, + }; diff --git a/lib/src/model/coursetable/course_table.dart b/lib/src/model/coursetable/course_table.dart new file mode 100644 index 00000000..797e8df8 --- /dev/null +++ b/lib/src/model/coursetable/course_table.dart @@ -0,0 +1,32 @@ +import 'package:json_annotation/json_annotation.dart'; + +import 'course.dart'; +import 'user.dart'; + +part 'course_table.g.dart'; + +@JsonSerializable() +class CourseTable { + final int year; + final int semester; + final List courses; + final User user; + late final Set weekdays = {}; + late final Set periods = {}; + + CourseTable({required this.year, required this.semester, required this.courses, required this.user}) { + weekdays.addAll(courses + .map((course) => course.coursePeriods.map((coursePeriod) => coursePeriod.weekday)) + .expand((element) => element)); + periods.addAll(courses + .map((course) => course.coursePeriods.map((coursePeriod) => coursePeriod.period)) + .expand((element) => element)); + } + + bool isPeriodInCourseTable(String period) => periods.contains(period); + + bool isWeekdayInCourseTable(int weekday) => weekdays.contains(weekday); + + factory CourseTable.fromJson(Map json) => _$CourseTableFromJson(json); + Map toJson() => _$CourseTableToJson(this); +} diff --git a/lib/src/model/coursetable/course_table.g.dart b/lib/src/model/coursetable/course_table.g.dart new file mode 100644 index 00000000..865d58d5 --- /dev/null +++ b/lib/src/model/coursetable/course_table.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'course_table.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CourseTable _$CourseTableFromJson(Map json) => CourseTable( + year: json['year'] as int, + semester: json['semester'] as int, + courses: (json['courses'] as List).map((e) => Course.fromJson(e as Map)).toList(), + user: User.fromJson(json['user'] as Map), + ); + +Map _$CourseTableToJson(CourseTable instance) => { + 'year': instance.year, + 'semester': instance.semester, + 'courses': instance.courses, + 'user': instance.user, + }; diff --git a/lib/src/model/coursetable/course_table_json.dart b/lib/src/model/coursetable/course_table_json.dart deleted file mode 100644 index 33333d2c..00000000 --- a/lib/src/model/coursetable/course_table_json.dart +++ /dev/null @@ -1,227 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:flutter_app/src/model/course/course_main_extra_json.dart'; -import 'package:flutter_app/src/model/json_init.dart'; -import 'package:json_annotation/json_annotation.dart'; -import 'package:sprintf/sprintf.dart'; - -part 'course_table_json.g.dart'; - -// ignore: constant_identifier_names -enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday, UnKnown } - -// ignore: constant_identifier_names -enum SectionNumber { T_1, T_2, T_3, T_4, T_N, T_5, T_6, T_7, T_8, T_9, T_A, T_B, T_C, T_D, T_UnKnown } - -@JsonSerializable() -class CourseTableJson { - SemesterJson courseSemester; //課程學期資料 - String studentId; - String studentName; - Map> courseInfoMap; - - CourseTableJson({this.courseSemester, this.courseInfoMap, this.studentId, this.studentName}) { - studentId = JsonInit.stringInit(studentId); - studentName = JsonInit.stringInit(studentName); - courseSemester = courseSemester ?? SemesterJson(); - if (courseInfoMap != null) { - courseInfoMap = courseInfoMap; - } else { - courseInfoMap = {}; - for (Day value in Day.values) { - courseInfoMap[value] = {}; - } - } - } - - int getTotalCredit() { - int credit = 0; - final List courseIdList = getCourseIdList(); - for (final courseId in courseIdList) { - credit += getCreditByCourseId(courseId); - } - return credit; - } - - int getCreditByCourseId(String courseId) { - for (Day day in Day.values) { - for (SectionNumber number in SectionNumber.values) { - CourseInfoJson courseDetail = courseInfoMap[day][number]; - if (courseDetail != null) { - if (courseDetail.main.course.id == courseId) { - String creditString = courseDetail.main.course.credits; - try { - return double.parse(creditString).toInt(); - } catch (e) { - return 0; - } - } - } - } - } - return 0; - } - - bool isDayInCourseTable(Day day) { - bool pass = false; - for (SectionNumber number in SectionNumber.values) { - if (courseInfoMap[day][number] != null) { - pass = true; - break; - } - } - return pass; - } - - bool isSectionNumberInCourseTable(SectionNumber number) { - bool pass = false; - for (Day day in Day.values) { - if (courseInfoMap[day].containsKey(number)) { - pass = true; - break; - } - } - return pass; - } - - factory CourseTableJson.fromJson(Map json) => _$CourseTableJsonFromJson(json); - - Map toJson() => _$CourseTableJsonToJson(this); - - @override - String toString() { - String courseInfoString = ""; - for (Day day in Day.values) { - for (SectionNumber number in SectionNumber.values) { - courseInfoString += "$day $number\n"; - courseInfoString += "${courseInfoMap[day][number]}\n"; - } - } - return sprintf("studentId :%s \n ---------courseSemester-------- \n%s \n---------courseInfo-------- \n%s \n", - [studentId, courseSemester.toString(), courseInfoString]); - } - - bool get isEmpty { - return studentId.isEmpty && courseSemester.isEmpty; - } - - CourseInfoJson getCourseDetailByTime(Day day, SectionNumber sectionNumber) { - return courseInfoMap[day][sectionNumber]; - } - - void setCourseDetailByTime(Day day, SectionNumber sectionNumber, CourseInfoJson courseInfo) { - if (day == Day.UnKnown) { - for (SectionNumber value in SectionNumber.values) { - if (courseInfo.main.course.id.isEmpty) { - continue; - } - if (!courseInfoMap[day].containsKey(value)) { - courseInfoMap[day][value] = courseInfo; - //Log.d( day.toString() + value.toString() + courseInfo.toString() ); - break; - } - } - } - /* else if (courseInfoMap[day].containsKey(sectionNumber)) { - throw Exception("衝堂"); - } */ - else { - courseInfoMap[day][sectionNumber] = courseInfo; - } - } - - bool setCourseDetailByTimeString(Day day, String sectionNumber, CourseInfoJson courseInfo) { - bool add = false; - for (SectionNumber value in SectionNumber.values) { - String time = value.toString().split("_")[1]; - if (sectionNumber.contains(time)) { - setCourseDetailByTime(day, value, courseInfo); - add = true; - } - } - return add; - } - - List getCourseIdList() { - List courseIdList = []; - for (Day day in Day.values) { - for (SectionNumber number in SectionNumber.values) { - CourseInfoJson courseInfo = courseInfoMap[day][number]; - if (courseInfo != null) { - String id = courseInfo.main.course.id; - if (!courseIdList.contains(id)) { - courseIdList.add(id); - } - } - } - } - return courseIdList; - } - - String getCourseNameByCourseId(String courseId) { - for (Day day in Day.values) { - for (SectionNumber number in SectionNumber.values) { - CourseInfoJson courseDetail = courseInfoMap[day][number]; - if (courseDetail != null) { - if (courseDetail.main.course.id == courseId) { - return courseDetail.main.course.name; - } - } - } - } - return null; - } - - CourseInfoJson getCourseInfoByCourseName(String courseName) { - for (Day day in Day.values) { - for (SectionNumber number in SectionNumber.values) { - CourseInfoJson courseDetail = courseInfoMap[day][number]; - if (courseDetail != null) { - if (courseDetail.main.course.name == courseName) { - return courseDetail; - } - } - } - } - return null; - } -} - -@JsonSerializable() -class CourseInfoJson { - CourseMainInfoJson main; - CourseExtraInfoJson extra; - - CourseInfoJson({this.main, this.extra}) { - main = main ?? CourseMainInfoJson(); - extra = extra ?? CourseExtraInfoJson(); - } - - bool get isEmpty { - return main.isEmpty && extra.isEmpty; - } - -/* - @override - bool operator ==(dynamic o) { - if( isEmpty || o.isEmpty || !(o is CourseInfoJson) ){ - return false; - }else{ - return ( main.course.id == o.main.course.id ); - } - } - - int get hashCode => hash2(main.hashCode, extra.hashCode); -*/ - - @override - String toString() { - return sprintf( - "---------main-------- \n%s \n" "---------extra-------- \n%s \n", [main.toString(), extra.toString()]); - } - - factory CourseInfoJson.fromJson(Map json) => _$CourseInfoJsonFromJson(json); - - Map toJson() => _$CourseInfoJsonToJson(this); -} diff --git a/lib/src/model/coursetable/course_table_json.g.dart b/lib/src/model/coursetable/course_table_json.g.dart deleted file mode 100644 index 3ca2e74f..00000000 --- a/lib/src/model/coursetable/course_table_json.g.dart +++ /dev/null @@ -1,105 +0,0 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'course_table_json.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -CourseTableJson _$CourseTableJsonFromJson(Map json) { - return CourseTableJson( - courseSemester: - json['courseSemester'] == null ? null : SemesterJson.fromJson(json['courseSemester'] as Map), - courseInfoMap: (json['courseInfoMap'] as Map)?.map( - (k, e) => MapEntry( - _$enumDecodeNullable(_$DayEnumMap, k), - (e as Map)?.map( - (k, e) => MapEntry(_$enumDecodeNullable(_$SectionNumberEnumMap, k), - e == null ? null : CourseInfoJson.fromJson(e as Map)), - )), - ), - studentId: json['studentId'] as String, - studentName: json['studentName'] as String, - ); -} - -Map _$CourseTableJsonToJson(CourseTableJson instance) => { - 'courseSemester': instance.courseSemester, - 'studentId': instance.studentId, - 'studentName': instance.studentName, - 'courseInfoMap': instance.courseInfoMap - ?.map((k, e) => MapEntry(_$DayEnumMap[k], e?.map((k, e) => MapEntry(_$SectionNumberEnumMap[k], e)))), - }; - -T _$enumDecode( - Map enumValues, - dynamic source, { - T unknownValue, -}) { - if (source == null) { - throw ArgumentError('A value must be provided. Supported values: ' - '${enumValues.values.join(', ')}'); - } - - final value = enumValues.entries.singleWhere((e) => e.value == source, orElse: () => null)?.key; - - if (value == null && unknownValue == null) { - throw ArgumentError('`$source` is not one of the supported values: ' - '${enumValues.values.join(', ')}'); - } - return value ?? unknownValue; -} - -T _$enumDecodeNullable( - Map enumValues, - dynamic source, { - T unknownValue, -}) { - if (source == null) { - return null; - } - return _$enumDecode(enumValues, source, unknownValue: unknownValue); -} - -const _$SectionNumberEnumMap = { - SectionNumber.T_1: 'T_1', - SectionNumber.T_2: 'T_2', - SectionNumber.T_3: 'T_3', - SectionNumber.T_4: 'T_4', - SectionNumber.T_N: 'T_N', - SectionNumber.T_5: 'T_5', - SectionNumber.T_6: 'T_6', - SectionNumber.T_7: 'T_7', - SectionNumber.T_8: 'T_8', - SectionNumber.T_9: 'T_9', - SectionNumber.T_A: 'T_A', - SectionNumber.T_B: 'T_B', - SectionNumber.T_C: 'T_C', - SectionNumber.T_D: 'T_D', - SectionNumber.T_UnKnown: 'T_UnKnown', -}; - -const _$DayEnumMap = { - Day.Monday: 'Monday', - Day.Tuesday: 'Tuesday', - Day.Wednesday: 'Wednesday', - Day.Thursday: 'Thursday', - Day.Friday: 'Friday', - Day.Saturday: 'Saturday', - Day.Sunday: 'Sunday', - Day.UnKnown: 'UnKnown', -}; - -CourseInfoJson _$CourseInfoJsonFromJson(Map json) { - return CourseInfoJson( - main: json['main'] == null ? null : CourseMainInfoJson.fromJson(json['main'] as Map), - extra: json['extra'] == null ? null : CourseExtraInfoJson.fromJson(json['extra'] as Map), - ); -} - -Map _$CourseInfoJsonToJson(CourseInfoJson instance) => { - 'main': instance.main, - 'extra': instance.extra, - }; diff --git a/lib/src/model/coursetable/user.dart b/lib/src/model/coursetable/user.dart new file mode 100644 index 00000000..3fc5f68d --- /dev/null +++ b/lib/src/model/coursetable/user.dart @@ -0,0 +1,20 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'user.g.dart'; + +@JsonSerializable() +class User { + final String id; + final String name; + final String className; + + User.origin() + : id = "", + name = "", + className = ""; + + User({required this.id, required this.name, required this.className}); + + factory User.fromJson(Map json) => _$UserFromJson(json); + Map toJson() => _$UserToJson(this); +} diff --git a/lib/src/model/coursetable/user.g.dart b/lib/src/model/coursetable/user.g.dart new file mode 100644 index 00000000..4942c5c0 --- /dev/null +++ b/lib/src/model/coursetable/user.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +User _$UserFromJson(Map json) => User( + id: json['id'] as String, + name: json['name'] as String, + className: json['className'] as String, + ); + +Map _$UserToJson(User instance) => { + 'id': instance.id, + 'name': instance.name, + 'className': instance.className, + }; diff --git a/lib/src/model/json_init.dart b/lib/src/model/json_init.dart index 321f9f99..459d6cd2 100644 --- a/lib/src/model/json_init.dart +++ b/lib/src/model/json_init.dart @@ -1,11 +1,17 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 class JsonInit { - static String stringInit(String value) { + static int intInit(String value) { + return value.replaceAll(RegExp(r"\s"), "").isNotEmpty ? int.parse(value) : 0; + } + + static double doubleInit(String value) { + return value.replaceAll(RegExp(r"\s"), "").isNotEmpty ? double.parse(value) : 0.00; + } + + static String stringInit(String? value) { return value ?? ""; } - static List listInit(List value) { - return value ?? [] as List; + static List listInit(List? value) { + return value ?? []; } } diff --git a/lib/src/model/setting/setting_json.dart b/lib/src/model/setting/setting_json.dart index 326cc6bd..2a01eadd 100644 --- a/lib/src/model/setting/setting_json.dart +++ b/lib/src/model/setting/setting_json.dart @@ -1,10 +1,11 @@ // TODO: remove sdk version selector after migrating to null-safety. // @dart=2.10 -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/model/json_init.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:sprintf/sprintf.dart'; +import '../coursetable/course_table.dart'; + part 'setting_json.g.dart'; @JsonSerializable() @@ -37,14 +38,14 @@ class SettingJson { @JsonSerializable() class CourseSettingJson { - CourseTableJson info; + CourseTable info; CourseSettingJson({this.info}) { - info = info ?? CourseTableJson(); + info = info; } bool get isEmpty { - return info.isEmpty; + return info.courses.isEmpty; } @override diff --git a/lib/src/model/setting/setting_json.g.dart b/lib/src/model/setting/setting_json.g.dart index 7717c429..7e85dad9 100644 --- a/lib/src/model/setting/setting_json.g.dart +++ b/lib/src/model/setting/setting_json.g.dart @@ -26,7 +26,7 @@ Map _$SettingJsonToJson(SettingJson instance) => json) { return CourseSettingJson( - info: json['info'] == null ? null : CourseTableJson.fromJson(json['info'] as Map), + info: json['info'] == null ? null : CourseTable.fromJson(json['info'] as Map), ); } diff --git a/lib/src/store/local_storage.dart b/lib/src/store/local_storage.dart index 400230a2..bd60bccb 100644 --- a/lib/src/store/local_storage.dart +++ b/lib/src/store/local_storage.dart @@ -6,15 +6,16 @@ import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter_app/src/connector/core/dio_connector.dart'; import 'package:flutter_app/src/model/course/course_score_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/model/setting/setting_json.dart'; import 'package:flutter_app/src/model/userdata/user_data_json.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:get/get.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:tat_core/tat_core.dart'; +import 'package:tat_core/core/zuvio/domain/login_credential.dart'; +import 'package:tat_core/core/zuvio/domain/user_info.dart'; -import '../model/course/course_class_json.dart'; +import '../model/course/course_semester.dart'; +import '../model/coursetable/course_table.dart'; class LocalStorage { LocalStorage._(); @@ -37,7 +38,7 @@ class LocalStorage { final _zUserInfoKey = 'ZUserInfoKey'; final _zLoginCredentialKey = 'ZLoginCredentialKey'; final _firstRun = {}; - final _courseTableList = []; + final _courseTableList = []; final _httpClientInterceptors = []; @@ -112,52 +113,56 @@ class LocalStorage { _courseTableList.clear(); if (readJsonList != null) { for (final readJson in readJsonList) { - _courseTableList.add(CourseTableJson.fromJson(json.decode(readJson))); + _courseTableList.add(CourseTable.fromJson(json.decode(readJson))); } } } - String getCourseNameByCourseId(String courseId) { - for (final courseDetail in _courseTableList) { - final name = courseDetail.getCourseNameByCourseId(courseId); - if (name != null) { - return name; + String getCourseNameByCourseId(int courseId) { + for (final courseTable in _courseTableList) { + final course = courseTable.courses.firstWhere((course) => course.id == courseId, orElse: () => null); + if (course != null) { + return course.name; } } return null; } - void removeCourseTable(CourseTableJson addCourseTable) { + void removeCourseTable(CourseTable courseTable) { _courseTableList.removeWhere( - (courseTable) => - courseTable.courseSemester == addCourseTable.courseSemester && - courseTable.studentId == addCourseTable.studentId, + (value) => + value.semester == courseTable.semester && + value.year == courseTable.year && + value.user.id == courseTable.user.id, ); } - void addCourseTable(CourseTableJson addCourseTable) { - removeCourseTable(addCourseTable); - _courseTableList.add(addCourseTable); + void addCourseTable(CourseTable courseTable) { + removeCourseTable(courseTable); + _courseTableList.add(courseTable); } - List getCourseTableList() { + List getCourseTableList() { _courseTableList.sort((a, b) { - if (a.studentId == b.studentId) { - return b.courseSemester.toString().compareTo(a.courseSemester.toString()); + if (a.user.id == b.user.id) { + if (a.year == b.year) { + return b.semester.compareTo(a.semester); + } + return a.year.compareTo(b.year); } - return a.studentId.compareTo(b.studentId); + return a.user.id.compareTo(b.user.id); }); return _courseTableList; } - CourseTableJson getCourseTable(String studentId, SemesterJson courseSemester) { - if (courseSemester == null || studentId == null || studentId.isEmpty) { + CourseTable getCourseTable(String studentId, int year, int semester) { + if (studentId == null || studentId.isEmpty) { return null; } return _courseTableList.firstWhereOrNull( - (courseTable) => courseTable.courseSemester == courseSemester && courseTable.studentId == studentId, + (courseTable) => courseTable.semester == semester && courseTable.year == year && courseTable.user.id == studentId, ); } diff --git a/lib/src/task/course/course_category_task.dart b/lib/src/task/course/course_category_task.dart index fe4eea3b..f78b6a34 100644 --- a/lib/src/task/course/course_category_task.dart +++ b/lib/src/task/course/course_category_task.dart @@ -1,7 +1,7 @@ // ignore_for_file: import_of_legacy_library_into_null_safe import 'package:flutter_app/src/connector/course_connector.dart'; -import 'package:flutter_app/src/model/course/course_syllabus_json.dart'; +import 'package:flutter_app/src/model/course/course_syllabus.dart'; import 'package:flutter_app/src/r.dart'; import '../task.dart'; diff --git a/lib/src/task/course/course_credit_info_task.dart b/lib/src/task/course/course_credit_info_task.dart index 4132a68b..b93ccc12 100644 --- a/lib/src/task/course/course_credit_info_task.dart +++ b/lib/src/task/course/course_credit_info_task.dart @@ -18,7 +18,7 @@ class CourseCreditInfoTask extends CourseSystemTask { final status = await super.execute(); if (status == TaskStatus.success) { super.onStart(R.current.searchingCreditInfo); - final value = await CourseConnector.getCreditInfo(code, creditName) as GraduationInformationJson?; + final value = await CourseConnector.getCreditInfo(code, creditName); super.onEnd(); if (value != null) { diff --git a/lib/src/task/course/course_department_task.dart b/lib/src/task/course/course_department_task.dart index d9b03cc4..21d5fd43 100644 --- a/lib/src/task/course/course_department_task.dart +++ b/lib/src/task/course/course_department_task.dart @@ -16,7 +16,7 @@ class CourseDepartmentTask extends CourseSystemTask> { final status = await super.execute(); if (status == TaskStatus.success) { super.onStart(R.current.searchingDepartment); - final value = await CourseConnector.getDepartmentList(code) as List>?; + final value = await CourseConnector.getDepartmentList(code); super.onEnd(); if (value != null) { diff --git a/lib/src/task/course/course_division_task.dart b/lib/src/task/course/course_division_task.dart index d7926ea2..7edf67b5 100644 --- a/lib/src/task/course/course_division_task.dart +++ b/lib/src/task/course/course_division_task.dart @@ -16,7 +16,7 @@ class CourseDivisionTask extends CourseSystemTask> { final status = await super.execute(); if (status == TaskStatus.success) { super.onStart(R.current.searchingDivision); - final value = await CourseConnector.getDivisionList(year) as List>?; + final value = await CourseConnector.getDivisionList(year); super.onEnd(); if (value != null) { diff --git a/lib/src/task/course/course_extra_info_task.dart b/lib/src/task/course/course_extra_info_task.dart deleted file mode 100644 index bec7e763..00000000 --- a/lib/src/task/course/course_extra_info_task.dart +++ /dev/null @@ -1,33 +0,0 @@ -// ignore_for_file: import_of_legacy_library_into_null_safe - -import 'package:flutter_app/src/connector/course_connector.dart'; -import 'package:flutter_app/src/model/course/course_main_extra_json.dart'; -import 'package:flutter_app/src/r.dart'; - -import '../task.dart'; -import 'course_system_task.dart'; - -class CourseExtraInfoTask extends CourseSystemTask { - final String id; - - CourseExtraInfoTask(this.id) : super("CourseExtraInfoTask"); - - @override - Future execute() async { - final status = await super.execute(); - - if (status == TaskStatus.success) { - super.onStart(R.current.getCourseDetail); - final value = await CourseConnector.getCourseExtraInfo(id) as CourseExtraInfoJson?; - super.onEnd(); - - if (value != null) { - result = value; - return TaskStatus.success; - } else { - return await super.onError(R.current.getCourseDetailError); - } - } - return status; - } -} diff --git a/lib/src/task/course/course_semester_task.dart b/lib/src/task/course/course_semester_task.dart index fada03bb..e41ca007 100644 --- a/lib/src/task/course/course_semester_task.dart +++ b/lib/src/task/course/course_semester_task.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/src/connector/course_connector.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; +import 'package:flutter_app/src/model/course/course_semester.dart'; import 'package:flutter_app/src/r.dart'; import 'package:get/get.dart'; import 'package:numberpicker/numberpicker.dart'; @@ -25,7 +25,7 @@ class CourseSemesterTask extends CourseSystemTask> { value = await _selectSemesterDialog(); } else { super.onStart(R.current.getCourseSemester); - value = await CourseConnector.getCourseSemester(id) as List?; + value = await CourseConnector.getCourseSemester(id); super.onEnd(); } diff --git a/lib/src/task/course/course_table_task.dart b/lib/src/task/course/course_table_task.dart index b0293c5f..057678f8 100644 --- a/lib/src/task/course/course_table_task.dart +++ b/lib/src/task/course/course_table_task.dart @@ -1,67 +1,45 @@ // ignore_for_file: import_of_legacy_library_into_null_safe import 'package:flutter_app/src/connector/course_connector.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; import 'package:flutter_app/src/util/language_util.dart'; +import '../../model/coursetable/course.dart'; +import '../../model/coursetable/course_table.dart'; +import '../../model/coursetable/user.dart'; import '../task.dart'; import 'course_system_task.dart'; -class CourseTableTask extends CourseSystemTask { +class CourseTableTask extends CourseSystemTask { final String studentId; - final SemesterJson semester; + final int year; + final int semester; - CourseTableTask(this.studentId, this.semester) : super("CourseTableTask"); + CourseTableTask(this.studentId, this.year, this.semester) : super("CourseTableTask"); @override Future execute() async { final status = await super.execute(); if (status == TaskStatus.success) { super.onStart(R.current.getCourse); - CourseMainInfo? value; - if (studentId.length == 5) { - value = await CourseConnector.getTWTeacherCourseMainInfoList(studentId, semester); + List courses; + User userInfo = await CourseConnector.getUserInfo(studentId, year, semester); + // TODO: Handle Teacher Situation. + if (LanguageUtil.getLangIndex() == LangEnum.zh) { + courses = await CourseConnector.getChineseCourses(studentId, year, semester); } else { - if (LanguageUtil.getLangIndex() == LangEnum.zh) { - value = await CourseConnector.getTWCourseMainInfoList(studentId, semester); - } else { - value = await CourseConnector.getENCourseMainInfoList(studentId, semester) as CourseMainInfo?; - } + courses = await CourseConnector.getEnglishCourses(studentId, year, semester); } super.onEnd(); - if (value != null) { - final courseTable = CourseTableJson(); - courseTable.courseSemester = semester; - courseTable.studentId = studentId; - courseTable.studentName = value.studentName; + final courseTable = CourseTable(year: year, semester: semester, courses: courses, user: userInfo); + LocalStorage.instance.addCourseTable(courseTable); + await LocalStorage.instance.saveCourseTableList(); - for (final courseMainInfo in value.json) { - final courseInfo = CourseInfoJson(); - bool add = false; - for (int i = 0; i < 7; i++) { - final day = Day.values[i]; - final time = courseMainInfo.course.time[day]; - courseInfo.main = courseMainInfo; - add |= courseTable.setCourseDetailByTimeString(day, time, courseInfo); - } - if (!add) { - courseTable.setCourseDetailByTime(Day.UnKnown, SectionNumber.T_UnKnown, courseInfo); - } - } - if (studentId == LocalStorage.instance.getAccount()) { - //只儲存自己的課表 - LocalStorage.instance.addCourseTable(courseTable); - await LocalStorage.instance.saveCourseTableList(); - } - result = courseTable; - return TaskStatus.success; - } else { - return super.onError(R.current.getCourseError); - } + result = courseTable; + return TaskStatus.success; + } else { + return super.onError(R.current.getCourseError); } - return status; } } diff --git a/lib/src/task/iplus/iplus_course_announcement_task.dart b/lib/src/task/iplus/iplus_course_announcement_task.dart index 5457432d..74d8fe87 100644 --- a/lib/src/task/iplus/iplus_course_announcement_task.dart +++ b/lib/src/task/iplus/iplus_course_announcement_task.dart @@ -10,7 +10,7 @@ import '../task.dart'; import 'iplus_system_task.dart'; class IPlusCourseAnnouncementTask extends IPlusSystemTask> { - final String id; + final int id; IPlusCourseAnnouncementTask(this.id) : super("IPlusCourseAnnouncementTask"); diff --git a/lib/src/task/iplus/iplus_course_file_task.dart b/lib/src/task/iplus/iplus_course_file_task.dart index 533ab2a6..07bd94c0 100644 --- a/lib/src/task/iplus/iplus_course_file_task.dart +++ b/lib/src/task/iplus/iplus_course_file_task.dart @@ -10,7 +10,7 @@ import '../task.dart'; import 'iplus_system_task.dart'; class IPlusCourseFileTask extends IPlusSystemTask> { - final String id; + final int id; IPlusCourseFileTask(this.id) : super("IPlusCourseFileTask"); diff --git a/lib/src/task/iplus/iplus_get_course_subscribe_task.dart b/lib/src/task/iplus/iplus_get_course_subscribe_task.dart index 8ae95517..f7944739 100644 --- a/lib/src/task/iplus/iplus_get_course_subscribe_task.dart +++ b/lib/src/task/iplus/iplus_get_course_subscribe_task.dart @@ -6,7 +6,7 @@ import 'package:flutter_app/src/task/iplus/iplus_system_task.dart'; import 'package:flutter_app/src/task/task.dart'; class IPlusGetCourseSubscribeTask extends IPlusSystemTask> { - final String id; + final int id; IPlusGetCourseSubscribeTask(this.id) : super("IPlusGetCourseSubscribeTask"); @@ -15,7 +15,7 @@ class IPlusGetCourseSubscribeTask extends IPlusSystemTask> final status = await super.execute(); if (status == TaskStatus.success) { super.onStart(R.current.searchSubscribe); - final courseBid = await ISchoolPlusConnector.getBid(id); + final courseBid = await ISchoolPlusConnector.getBid(); final openNotifications = await ISchoolPlusConnector.getCourseSubscribe(courseBid); super.onEnd(); result = { diff --git a/lib/ui/other/route_utils.dart b/lib/ui/other/route_utils.dart index a7bba903..b3aa9595 100644 --- a/lib/ui/other/route_utils.dart +++ b/lib/ui/other/route_utils.dart @@ -5,7 +5,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/src/connector/core/dio_connector.dart'; import 'package:flutter_app/src/controllers/zuvio_auth_controller.dart'; import 'package:flutter_app/src/controllers/zuvio_auto_roll_call_schedule_controller.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/ui/pages/coursedetail/course_detail_page.dart'; import 'package:flutter_app/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart'; import 'package:flutter_app/ui/pages/fileviewer/file_viewer_page.dart'; @@ -24,6 +23,8 @@ import 'package:flutter_app/ui/screen/login_screen.dart'; import 'package:flutter_app/ui/screen/main_screen.dart'; import 'package:get/get.dart'; +import '../../src/model/coursetable/course.dart'; + class RouteUtils { static Transition transition = (Platform.isAndroid) ? Transition.downToUp : Transition.cupertino; @@ -67,9 +68,9 @@ class RouteUtils { ); } - static Future? toISchoolPage(String studentId, CourseInfoJson courseInfo) { + static Future? toISchoolPage(String studentId, Course course) { return Get.to( - () => ISchoolPage(studentId, courseInfo), + () => ISchoolPage(studentId, course), transition: transition, ); } @@ -122,15 +123,15 @@ class RouteUtils { DioConnector.instance.getAlice(navigatorKey: Get.key).showInspector(); } - static Future? toIPlusAnnouncementDetailPage(CourseInfoJson courseInfo, Map detail) { + static Future? toIPlusAnnouncementDetailPage(Course course, Map detail) { return Get.to( - () => IPlusAnnouncementDetailPage(courseInfo, detail), + () => IPlusAnnouncementDetailPage(course, detail), transition: transition, ); } - static Future? toVideoPlayer(String url, CourseInfoJson courseInfo, String name) => Get.to( - () => ClassVideoPlayer(url, courseInfo, name), + static Future? toVideoPlayer(String url, Course course, String name) => Get.to( + () => ClassVideoPlayer(url, course, name), transition: transition, ); diff --git a/lib/ui/pages/coursedetail/course_detail_page.dart b/lib/ui/pages/coursedetail/course_detail_page.dart index 794c6240..f187dfd1 100644 --- a/lib/ui/pages/coursedetail/course_detail_page.dart +++ b/lib/ui/pages/coursedetail/course_detail_page.dart @@ -1,8 +1,6 @@ // TODO: remove sdk version selector after migrating to null-safety. // @dart=2.10 import 'package:flutter/material.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/providers/app_provider.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; @@ -13,11 +11,13 @@ import 'package:flutter_app/ui/pages/coursedetail/tab_page.dart'; import 'package:get/get.dart'; import 'package:provider/provider.dart'; +import '../../../src/model/coursetable/course.dart'; + class ISchoolPage extends StatefulWidget { - final CourseInfoJson courseInfo; + final Course course; final String studentId; - const ISchoolPage(this.studentId, this.courseInfo, {Key key}) : super(key: key); + const ISchoolPage(this.studentId, this.course, {Key key}) : super(key: key); @override State createState() => _ISchoolPageState(); @@ -33,12 +33,19 @@ class _ISchoolPageState extends State with SingleTickerProviderStat void initState() { super.initState(); tabPageList = TabPageList(); - tabPageList.add(TabPage(R.current.course, Icons.info, CourseInfoPage(widget.studentId, widget.courseInfo))); + tabPageList.add(TabPage( + R.current.course, + Icons.info, + CourseInfoPage( + widget.studentId, + widget.course, + key: null, + ))); if (widget.studentId == LocalStorage.instance.getAccount()) { - tabPageList.add(TabPage( - R.current.announcement, Icons.announcement, IPlusAnnouncementPage(widget.studentId, widget.courseInfo))); tabPageList.add( - TabPage(R.current.fileAndVideo, Icons.file_download, IPlusFilePage(widget.studentId, widget.courseInfo))); + TabPage(R.current.announcement, Icons.announcement, IPlusAnnouncementPage(widget.studentId, widget.course))); + tabPageList + .add(TabPage(R.current.fileAndVideo, Icons.file_download, IPlusFilePage(widget.studentId, widget.course))); } _tabController = TabController(vsync: this, length: tabPageList.length); @@ -63,8 +70,7 @@ class _ISchoolPageState extends State with SingleTickerProviderStat } Widget tabPageView() { - CourseMainJson course = widget.courseInfo.main.course; - + Course course = widget.course; return DefaultTabController( length: tabPageList.length, child: Scaffold( diff --git a/lib/ui/pages/coursedetail/screen/course_info_page.dart b/lib/ui/pages/coursedetail/screen/course_info_page.dart index 6ff49c14..c17d923e 100644 --- a/lib/ui/pages/coursedetail/screen/course_info_page.dart +++ b/lib/ui/pages/coursedetail/screen/course_info_page.dart @@ -1,24 +1,20 @@ -// TODO: remove sdk version selector after migrating to null-safety. -// @dart=2.10 import 'dart:async'; import 'package:back_button_interceptor/back_button_interceptor.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_app/src/model/course/course_main_extra_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/r.dart'; -import 'package:flutter_app/src/task/course/course_extra_info_task.dart'; -import 'package:flutter_app/src/task/task_flow.dart'; import 'package:flutter_app/ui/other/route_utils.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:get/get.dart'; import 'package:sprintf/sprintf.dart'; +import '../../../../src/model/coursetable/course.dart'; + class CourseInfoPage extends StatefulWidget { - final CourseInfoJson courseInfo; + final Course course; final String studentId; - const CourseInfoPage(this.studentId, this.courseInfo, {Key key}) : super(key: key); + const CourseInfoPage(this.studentId, this.course, {Key? key}) : super(key: key); final int courseInfoWithAlpha = 0x44; @@ -27,8 +23,7 @@ class CourseInfoPage extends StatefulWidget { } class _CourseInfoPageState extends State with AutomaticKeepAliveClientMixin { - CourseMainInfoJson courseMainInfo; - CourseExtraInfoJson courseExtraInfo; + late Course course; bool isLoading = true; final List courseData = []; final List listItem = []; @@ -58,38 +53,32 @@ class _CourseInfoPageState extends State with AutomaticKeepAlive } void _addTask() async { - courseMainInfo = widget.courseInfo.main; - final courseId = courseMainInfo.course.id; - final taskFlow = TaskFlow(); - final task = CourseExtraInfoTask(courseId); - taskFlow.addTask(task); - if (await taskFlow.start()) { - courseExtraInfo = task.result; - } - widget.courseInfo.extra = courseExtraInfo; - courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.courseId, courseMainInfo.course.id]))); - courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.courseName, courseMainInfo.course.name]))); - courseData.add(_buildCourseInfo(sprintf("%s: %s ", [R.current.credit, courseMainInfo.course.credits]))); - courseData.add(_buildCourseInfo(sprintf("%s: %s ", [R.current.category, courseExtraInfo.course.category]))); + course = widget.course; + courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.courseId, course.id]))); + courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.courseName, course.name]))); + courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.credit, course.credit]))); + courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.category, course.category]))); courseData.add( _buildCourseInfoWithButton( - sprintf("%s: %s", [R.current.instructor, courseMainInfo.getTeacherName()]), + sprintf("%04s: %s", [R.current.instructor, course.teachers.join(" ")]), R.current.syllabus, - courseMainInfo.course.scheduleHref, + course.syllabusLink, ), ); - courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.startClass, courseMainInfo.getOpenClassName()]))); + courseData.add(_buildCourseInfo(sprintf("%s: %s", [R.current.startClass, course.classNames.join(" ")]))); courseData.add(_buildMultiButtonInfo( sprintf("%s: ", [R.current.classroom]), R.current.classroomUse, - courseMainInfo.getClassroomNameList(), - courseMainInfo.getClassroomHrefList(), + course.classrooms, + course.classrooms, )); listItem.removeRange(0, listItem.length); listItem.add(_buildInfoTitle(R.current.courseData)); listItem.addAll(courseData); - + // listItem.add(_buildCourseCard(course)); + // listItem.add(_buildInfoTitle("課程修課資訊")); + // listItem.add(_buildCourseApplyCard(course)); isLoading = false; setState(() {}); } @@ -147,7 +136,8 @@ class _CourseInfoPageState extends State with AutomaticKeepAlive child: Row( children: [ const Icon(Icons.details), - Expanded( + Container( + alignment: const Align(alignment: Alignment.topCenter).alignment, child: Text( text, style: textStyle, @@ -197,7 +187,7 @@ class _CourseInfoPageState extends State with AutomaticKeepAlive Widget _buildInfoTitle(String title) { TextStyle textStyle = const TextStyle(fontSize: 24); return Container( - padding: const EdgeInsets.only(top: 5, bottom: 5), + padding: const EdgeInsets.only(top: 20, bottom: 20), child: Row( children: [ Text( diff --git a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart index 243617c2..df08559c 100644 --- a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart +++ b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_detail_page.dart @@ -4,7 +4,6 @@ import 'package:back_button_interceptor/back_button_interceptor.dart'; import 'package:flutter/material.dart'; import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/file/file_download.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/util/html_utils.dart'; import 'package:flutter_app/ui/other/list_view_animator.dart'; @@ -13,11 +12,13 @@ import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart import 'package:get/get.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../../../../src/model/coursetable/course.dart'; + class IPlusAnnouncementDetailPage extends StatefulWidget { final Map data; - final CourseInfoJson courseInfo; + final Course course; - const IPlusAnnouncementDetailPage(this.courseInfo, this.data, {Key key}) : super(key: key); + const IPlusAnnouncementDetailPage(this.course, this.data, {Key key}) : super(key: key); @override State createState() => _IPlusAnnouncementDetailPage(); @@ -50,7 +51,7 @@ class _IPlusAnnouncementDetailPage extends State { leading: BackButton( onPressed: () => Get.back(), ), - title: Text(widget.courseInfo.main.course.name), + title: Text(widget.course.name), actions: [ PopupMenuButton( onSelected: (result) { @@ -191,7 +192,7 @@ class _IPlusAnnouncementDetailPage extends State { } void _downloadFile(String url, String name) async { - String courseName = widget.courseInfo.main.course.name; + String courseName = widget.course.name; await FileDownload.download(url, courseName, name); } diff --git a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_page.dart b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_page.dart index 41264bbf..540c66bb 100644 --- a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_page.dart +++ b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_announcement_page.dart @@ -1,7 +1,6 @@ // TODO: remove sdk version selector after migrating to null-safety. // @dart=2.10 import 'package:flutter/material.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/model/ischoolplus/ischool_plus_announcement_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; @@ -12,11 +11,13 @@ import 'package:flutter_app/src/task/iplus/iplus_course_announcement_task.dart'; import 'package:flutter_app/src/task/task_flow.dart'; import 'package:flutter_app/ui/other/route_utils.dart'; +import '../../../../../src/model/coursetable/course.dart'; + class IPlusAnnouncementPage extends StatefulWidget { - final CourseInfoJson courseInfo; + final Course course; final String studentId; - const IPlusAnnouncementPage(this.studentId, this.courseInfo, {Key key}) : super(key: key); + const IPlusAnnouncementPage(this.studentId, this.course, {Key key}) : super(key: key); @override State createState() => _IPlusAnnouncementPage(); @@ -41,7 +42,7 @@ class _IPlusAnnouncementPage extends State with Automatic void _addTask() async { //第一次 - String courseId = widget.courseInfo.main.course.id; + int courseId = widget.course.id; TaskFlow taskFlow = TaskFlow(); var task = IPlusCourseAnnouncementTask(courseId); var getTask = IPlusGetCourseSubscribeTask(courseId); @@ -67,7 +68,7 @@ class _IPlusAnnouncementPage extends State with Automatic if (await taskFlow.start()) { detail = task.result; } - RouteUtils.toIPlusAnnouncementDetailPage(widget.courseInfo, detail); + RouteUtils.toIPlusAnnouncementDetailPage(widget.course, detail); } @override diff --git a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_file_page.dart b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_file_page.dart index b066f2dd..eb2d194f 100644 --- a/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_file_page.dart +++ b/lib/ui/pages/coursedetail/screen/ischoolplus/iplus_file_page.dart @@ -6,7 +6,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/src/connector/ischool_plus_connector.dart'; import 'package:flutter_app/src/file/file_download.dart'; import 'package:flutter_app/src/file/file_store.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/model/ischoolplus/course_file_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; @@ -20,13 +19,15 @@ import 'package:flutter_app/ui/other/route_utils.dart'; import 'package:sprintf/sprintf.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../../../../src/model/coursetable/course.dart'; + class IPlusFilePage extends StatefulWidget { - final CourseInfoJson courseInfo; + final Course course; final String studentId; const IPlusFilePage( this.studentId, - this.courseInfo, { + this.course, { super.key, }); @@ -71,7 +72,7 @@ class _IPlusFilePage extends State with AutomaticKeepAliveClientM void _addTask() async { await Future.delayed(const Duration(microseconds: 500)); - final courseId = widget.courseInfo.main.course.id; + final courseId = widget.course.id; final taskFlow = TaskFlow(); final task = IPlusCourseFileTask(courseId); @@ -230,7 +231,7 @@ class _IPlusFilePage extends State with AutomaticKeepAliveClientM Future _downloadOneFile(int index, [showToast = true]) async { final courseFile = courseFileList[index]; final fileType = courseFile.fileType[0]; - final dirName = widget.courseInfo.main.course.name; + final dirName = widget.course.name; String url = ""; String referer = ""; @@ -266,7 +267,7 @@ class _IPlusFilePage extends State with AutomaticKeepAliveClientM errorDialogParameter.dialogType = DialogType.info; errorDialogParameter.okButtonText = R.current.sure; errorDialogParameter.onOkButtonClicked = - () => RouteUtils.toVideoPlayer(urlParse.toString(), widget.courseInfo, courseFile.name); + () => RouteUtils.toVideoPlayer(urlParse.toString(), widget.course, courseFile.name); MsgDialog(errorDialogParameter).show(); } else { await FileDownload.download(url, dirName, courseFile.name, referer); diff --git a/lib/ui/pages/coursetable/course_table_control.dart b/lib/ui/pages/coursetable/course_table_control.dart index bb60f4f7..53347384 100644 --- a/lib/ui/pages/coursetable/course_table_control.dart +++ b/lib/ui/pages/coursetable/course_table_control.dart @@ -2,9 +2,12 @@ // @dart=2.10 import 'package:flutter/material.dart'; import 'package:flutter_app/src/config/app_colors.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; +import 'package:flutter_app/src/model/coursetable/course_period.dart'; import 'package:flutter_app/src/r.dart'; +import '../../../src/model/coursetable/course.dart'; +import '../../../src/model/coursetable/course_table.dart'; + class CourseTableControl { bool isHideSaturday = false; bool isHideSunday = false; @@ -14,15 +17,15 @@ class CourseTableControl { bool isHideB = false; bool isHideC = false; bool isHideD = false; - CourseTableJson courseTable; + CourseTable courseTable; List dayStringList = [ + R.current.Sunday, R.current.Monday, R.current.Tuesday, R.current.Wednesday, R.current.Thursday, R.current.Friday, R.current.Saturday, - R.current.Sunday, R.current.UnKnown ]; List timeList = [ @@ -47,16 +50,15 @@ class CourseTableControl { static int sectionLength = 14; Map colorMap; - void set(CourseTableJson value) { + void set(CourseTable value) { courseTable = value; - isHideSaturday = !courseTable.isDayInCourseTable(Day.Saturday); - isHideSunday = !courseTable.isDayInCourseTable(Day.Sunday); - isHideUnKnown = !courseTable.isDayInCourseTable(Day.UnKnown); - isHideN = !courseTable.isSectionNumberInCourseTable(SectionNumber.T_N); - isHideA = (!courseTable.isSectionNumberInCourseTable(SectionNumber.T_A)); - isHideB = (!courseTable.isSectionNumberInCourseTable(SectionNumber.T_B)); - isHideC = (!courseTable.isSectionNumberInCourseTable(SectionNumber.T_C)); - isHideD = (!courseTable.isSectionNumberInCourseTable(SectionNumber.T_D)); + isHideSaturday = !courseTable.isWeekdayInCourseTable(6); + isHideSunday = !courseTable.isWeekdayInCourseTable(0); + isHideN = !courseTable.isPeriodInCourseTable("N"); + isHideA = !courseTable.isPeriodInCourseTable("A"); + isHideB = !courseTable.isPeriodInCourseTable("B"); + isHideC = !courseTable.isPeriodInCourseTable("C"); + isHideD = !courseTable.isPeriodInCourseTable("D"); isHideA &= (isHideB & isHideC & isHideD); isHideB &= (isHideC & isHideD); isHideC &= isHideD; @@ -66,37 +68,37 @@ class CourseTableControl { List get getDayIntList { List intList = []; for (int i = 0; i < dayLength; i++) { - if (isHideSaturday && i == 5) continue; - if (isHideSunday && i == 6) continue; + if (isHideSaturday && i == 6) continue; + if (isHideSunday && i == 0) continue; if (isHideUnKnown && i == 7) continue; intList.add(i); } return intList; } - CourseInfoJson getCourseInfo(int intDay, int intNumber) { - final day = Day.values[intDay]; - final number = SectionNumber.values[intNumber]; - - if (courseTable == null) { - return null; + Course getCourse(int weekday, int period) { + if (weekday == 7) { + return getUnknownCourse(period); } - - return courseTable?.courseInfoMap[day][number]; + return courseTable.courses.firstWhere( + (course) => course.coursePeriods + .any((coursePeriod) => coursePeriod.period == getSectionString(period) && coursePeriod.weekday == weekday), + orElse: () => null); } - Color getCourseInfoColor(int intDay, int intNumber) { - final courseInfo = getCourseInfo(intDay, intNumber); - + Color getCourseInfoColor(int weekday, int period) { + Course course = getCourse(weekday, period); if (colorMap == null) { return Colors.white; } + if (course == null) { + return Colors.white; + } + for (final key in colorMap.keys) { - if (courseInfo != null) { - if (key == courseInfo.main.course.id) { - return colorMap[key]; - } + if (key == course.id.toString()) { + return colorMap[key]; } } @@ -105,7 +107,7 @@ class CourseTableControl { void _initColorList() { colorMap = {}; - List courseInfoList = courseTable.getCourseIdList(); + List courseInfoList = courseTable.courses.map((course) => course.id.toString()).toList(); int colorCount = courseInfoList.length; colorCount = (colorCount == 0) ? 1 : colorCount; @@ -116,6 +118,14 @@ class CourseTableControl { } } + List get getCoursePeriodList { + final Set coursePeriodSet = {}; + for (Course course in courseTable.courses) { + coursePeriodSet.addAll(course.coursePeriods); + } + return coursePeriodSet.toList(); + } + List get getSectionIntList { List intList = []; for (int i = 0; i < sectionLength; i++) { @@ -140,4 +150,9 @@ class CourseTableControl { String getSectionString(int section) { return sectionStringList[section]; } + + Course getUnknownCourse(int period) { + final nonPeriodCourses = courseTable.courses.where((course) => course.coursePeriods.isEmpty); + return period >= nonPeriodCourses.length ? null : nonPeriodCourses.elementAt(period); + } } diff --git a/lib/ui/pages/coursetable/course_table_page.dart b/lib/ui/pages/coursetable/course_table_page.dart index b9f382dc..c6dceb85 100644 --- a/lib/ui/pages/coursetable/course_table_page.dart +++ b/lib/ui/pages/coursetable/course_table_page.dart @@ -10,8 +10,7 @@ import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/config/app_config.dart'; -import 'package:flutter_app/src/model/course/course_class_json.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; +import 'package:flutter_app/src/model/course/course_semester.dart'; import 'package:flutter_app/src/model/userdata/user_data_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; @@ -29,6 +28,9 @@ import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sprintf/sprintf.dart'; +import '../../../src/model/coursetable/course.dart'; +import '../../../src/model/coursetable/course_table.dart'; + class CourseTablePage extends StatefulWidget { const CourseTablePage({Key key}) : super(key: key); @@ -41,7 +43,7 @@ class _CourseTablePageState extends State { final FocusNode _studentFocus = FocusNode(); final GlobalKey _key = GlobalKey(); bool isLoading = true; - CourseTableJson courseTableData; + CourseTable courseTableData; static double dayHeight = 25; static double studentIdHeight = 40; static double courseHeight = 60; @@ -69,7 +71,7 @@ class _CourseTablePageState extends State { if (!LocalStorage.instance.getFirstUse(LocalStorage.courseNotice, timeOut: 15 * 60)) { return; } - if (LocalStorage.instance.getAccount() != LocalStorage.instance.getCourseSetting().info.studentId) { + if (LocalStorage.instance.getAccount() != LocalStorage.instance.getCourseSetting().info.user.id) { //只有顯示自己的課表時才會檢查新公告 return; } @@ -87,8 +89,8 @@ class _CourseTablePageState extends State { v = v ?? []; for (int i = 0; i < v.length; i++) { String courseName = v[i]; - CourseInfoJson courseInfo = courseTableData.getCourseInfoByCourseName(courseName); - if (courseInfo != null) { + Course course = courseTableData.courses.firstWhere((course) => course.name == courseName, orElse: () => null); + if (course != null) { value.add(courseName); } } @@ -106,7 +108,8 @@ class _CourseTablePageState extends State { child: Text(value[index]), onPressed: () { String courseName = value[index]; - CourseInfoJson courseInfo = courseTableData.getCourseInfoByCourseName(courseName); + Course courseInfo = + courseTableData.courses.firstWhere((course) => course.name == courseName, orElse: () => null); if (courseInfo != null) { _showCourseDetail(courseInfo); } else { @@ -154,8 +157,8 @@ class _CourseTablePageState extends State { final renderObject = _key.currentContext.findRenderObject(); courseHeight = (renderObject.semanticBounds.size.height - studentIdHeight - dayHeight) / showCourseTableNum; final courseTable = LocalStorage.instance.getCourseSetting().info; - if (courseTable == null || courseTable.isEmpty) { - _getCourseTable(studentId: courseTable.studentId, semesterSetting: courseTable.courseSemester); + if (courseTable == null || courseTable.courses.isEmpty) { + _getCourseTable(studentId: "", year: 0, semester: 0); } else { _showCourseTable(courseTable); } @@ -170,32 +173,29 @@ class _CourseTablePageState extends State { } } - void _getCourseTable({SemesterJson semesterSetting, String studentId, bool refresh = false}) async { + void _getCourseTable({int year, int semester, String studentId, bool refresh = false}) async { await Future.delayed(const Duration(microseconds: 100)); //等待頁面刷新 UserDataJson userData = LocalStorage.instance.getUserData(); studentId = studentId?.trim() ?? ''; studentId = (studentId.isEmpty ? null : studentId) ?? userData.account; - if (courseTableData?.studentId != studentId) { + if (userData.account != studentId) { LocalStorage.instance.clearSemesterJsonList(); //需重設因為更換了studentId } - SemesterJson semesterJson; - if (semesterSetting == null || semesterSetting.semester.isEmpty || semesterSetting.year.isEmpty) { + + if (year == 0 && semester == 0) { await _getSemesterList(studentId); - semesterJson = LocalStorage.instance.getSemesterJsonItem(0); - } else { - semesterJson = semesterSetting; - } - if (semesterJson == null) { - return; + SemesterJson semesterJson = LocalStorage.instance.getSemesterJsonItem(0); + year = int.parse(semesterJson.year); + semester = int.parse(semesterJson.semester); } - CourseTableJson courseTable; + CourseTable courseTable; if (!refresh) { - courseTable = LocalStorage.instance.getCourseTable(studentId, semesterSetting); //去取找是否已經暫存 + courseTable = LocalStorage.instance.getCourseTable(studentId, year, semester); //去取找是否已經暫存 } if (courseTable == null) { TaskFlow taskFlow = TaskFlow(); - var task = CourseTableTask(studentId, semesterJson); + var task = CourseTableTask(studentId, year, semester); taskFlow.addTask(task); if (await taskFlow.start()) { courseTable = task.result; @@ -209,13 +209,13 @@ class _CourseTablePageState extends State { } } - Widget _getSemesterItem(SemesterJson semester) { - final semesterString = "${semester.year}-${semester.semester}"; + Widget _getSemesterItem(int year, int semester) { + final semesterString = "$year-$semester"; return TextButton( child: Text(semesterString), onPressed: () { Get.back(); - _getCourseTable(semesterSetting: semester, studentId: _studentIdControl.text); //取得課表 + _getCourseTable(year: year, semester: semester, studentId: _studentIdControl.text); //取得課表 }, ); } @@ -244,7 +244,8 @@ class _CourseTablePageState extends State { child: ListView.builder( itemCount: semesterList?.length ?? 0, shrinkWrap: true, - itemBuilder: (context, index) => _getSemesterItem(semesterList[index]), + itemBuilder: (context, index) => + _getSemesterItem(int.parse(semesterList[index].year), int.parse(semesterList[index].semester)), ), ), ), @@ -255,7 +256,10 @@ class _CourseTablePageState extends State { _onPopupMenuSelect(int value) { switch (value) { case 0: - final credit = courseTableData?.getTotalCredit()?.toString(); + final credit = courseTableData?.courses + ?.map((course) => course.credit) + ?.toList() + ?.reduce((value, element) => value + element); if (credit != null) { MyToast.show(sprintf("%s:%s", [R.current.credit, credit])); } @@ -281,7 +285,7 @@ class _CourseTablePageState extends State { } void _loadFavorite() async { - List value = LocalStorage.instance.getCourseTableList(); + List value = LocalStorage.instance.getCourseTableList(); if (value.isEmpty) { MyToast.show(R.current.noAnyFavorite); return; @@ -319,12 +323,8 @@ class _CourseTablePageState extends State { child: SizedBox( height: 50, child: TextButton( - child: Text(sprintf("%s %s %s-%s", [ - value[index].studentId, - value[index].studentName, - value[index].courseSemester.year, - value[index].courseSemester.semester - ])), + child: Text(sprintf("%s %s %d-%d", + [value[index].user.id, value[index].user.name, value[index].year, value[index].semester])), onPressed: () { LocalStorage.instance.getCourseSetting().info = value[index]; //儲存課表 LocalStorage.instance.saveCourseSetting(); @@ -350,8 +350,9 @@ class _CourseTablePageState extends State { @override Widget build(BuildContext context) { - final semesterSetting = courseTableData?.courseSemester ?? SemesterJson(); - final semesterString = "${semesterSetting.year}-${semesterSetting.semester}"; + final year = courseTableData == null ? 0 : courseTableData.year; + final semester = courseTableData == null ? 0 : courseTableData.semester; + final semesterString = "$year-$semester"; return Scaffold( resizeToAvoidBottomInset: false, @@ -368,7 +369,7 @@ class _CourseTablePageState extends State { ), ) : const SizedBox.shrink(), - (!isLoading && LocalStorage.instance.getAccount() != courseTableData?.studentId) + (!isLoading && LocalStorage.instance.getAccount() != courseTableData.user.id) ? Padding( padding: const EdgeInsets.only( right: 20, @@ -388,7 +389,8 @@ class _CourseTablePageState extends State { ), child: InkWell( onTap: () => _getCourseTable( - semesterSetting: courseTableData?.courseSemester, + year: year, + semester: semester, studentId: _studentIdControl.text, refresh: true, ), @@ -563,11 +565,11 @@ class _CourseTablePageState extends State { final isDarkMode = Get.isDarkMode; for (final day in courseTableControl.getDayIntList) { - final courseInfo = courseTableControl.getCourseInfo(day, section) ?? CourseInfoJson(); - final color = courseTableControl.getCourseInfoColor(day, section); + final course = courseTableControl.getCourse(day, section); + final courseInfoColor = courseTableControl.getCourseInfoColor(day, section); widgetList.add( Expanded( - child: (courseInfo.isEmpty) + child: (course == null) ? const SizedBox.shrink() : Card( elevation: 0, @@ -575,14 +577,14 @@ class _CourseTablePageState extends State { child: Container( decoration: BoxDecoration( borderRadius: const BorderRadius.all(Radius.circular(5)), - color: color, + color: courseInfoColor, ), child: Material( color: Colors.transparent, child: InkWell( borderRadius: const BorderRadius.all(Radius.circular(5)), highlightColor: isDarkMode ? Colors.white : Colors.black12, - onTap: () => showCourseDetailDialog(section, courseInfo), + onTap: () => showCourseDetailDialog(courseTableControl.getTimeString(section), course), child: Stack( children: [ Align( @@ -590,7 +592,7 @@ class _CourseTablePageState extends State { child: Padding( padding: const EdgeInsets.all(2), child: AutoSizeText( - courseInfo.main.course.name, + course.name, style: const TextStyle( color: Colors.black, fontSize: 14, @@ -622,12 +624,11 @@ class _CourseTablePageState extends State { } //顯示課程對話框 - void showCourseDetailDialog(int section, CourseInfoJson courseInfo) { + void showCourseDetailDialog(String timePeriod, Course course) { _unFocusStudentInput(); - final course = courseInfo.main.course; - final classroomName = courseInfo.main.getClassroomName(); - final teacherName = courseInfo.main.getTeacherName(); - final studentId = LocalStorage.instance.getCourseSetting().info.studentId; + final classroomName = course.classrooms.join(" "); + final teacherName = course.teachers.join(" "); + final studentId = LocalStorage.instance.getCourseSetting().info.user.id; setState(() { _studentIdControl.text = studentId; }); @@ -642,20 +643,20 @@ class _CourseTablePageState extends State { GestureDetector( child: Text(sprintf("%s : %s", [R.current.courseId, course.id])), onLongPress: () async { - course.id = await _showEditDialog(course.id); + course.id = int.parse(await _showEditDialog(course.id.toString())); await LocalStorage.instance.saveOtherSetting(); setState(() {}); }, ), - Text(sprintf("%s : %s", [R.current.time, courseTableControl.getTimeString(section)])), + Text(sprintf("%s : %s", [R.current.time, timePeriod])), Text(sprintf("%s : %s", [R.current.location, classroomName])), Text(sprintf("%s : %s", [R.current.instructor, teacherName])), ], ), - actions: courseInfo.main.course.id.isNotEmpty + actions: !course.isEmpty() ? [ TextButton.icon( - onPressed: () => _showCourseDetail(courseInfo), + onPressed: () => _showCourseDetail(course), icon: const Icon(Icons.add_outlined), label: Text(R.current.details), ), @@ -702,17 +703,18 @@ class _CourseTablePageState extends State { return v ?? value; } - void _showCourseDetail(CourseInfoJson courseInfo) { - final course = courseInfo.main.course; + void _showCourseDetail(Course course) { Get.back(); - final studentId = LocalStorage.instance.getCourseSetting().info.studentId; - if (course.id.isEmpty) { + final studentId = LocalStorage.instance.getCourseSetting().info.user.id; + if (course.isEmpty()) { MyToast.show(course.name + R.current.noSupport); } else { - RouteUtils.toISchoolPage(studentId, courseInfo).then((value) { + RouteUtils.toISchoolPage(studentId, course).then((value) { if (value != null) { - final semesterSetting = LocalStorage.instance.getCourseSetting().info.courseSemester; - _getCourseTable(semesterSetting: semesterSetting, studentId: value); + final courseTable = LocalStorage.instance.getCourseSetting().info; + final year = courseTable.year; + final semester = courseTable.semester; + _getCourseTable(year: year, semester: semester, studentId: value); } }); } @@ -723,13 +725,13 @@ class _CourseTablePageState extends State { _studentFocus.unfocus(); } - void _showCourseTable(CourseTableJson courseTable) async { + void _showCourseTable(CourseTable courseTable) async { if (courseTable == null) { return; } getCourseNotice(); //查詢訂閱的課程是否有公告 courseTableData = courseTable; - _studentIdControl.text = courseTable.studentId; + _studentIdControl.text = courseTable.user.id; _unFocusStudentInput(); setState(() { isLoading = true; @@ -739,7 +741,8 @@ class _CourseTablePageState extends State { setState(() { isLoading = false; }); - favorite = (LocalStorage.instance.getCourseTable(courseTable.studentId, courseTable.courseSemester) != null); + favorite = + (LocalStorage.instance.getCourseTable(courseTable.user.id, courseTable.year, courseTable.semester) != null); if (favorite) { LocalStorage.instance.addCourseTable(courseTableData); } diff --git a/lib/ui/pages/score/score_page.dart b/lib/ui/pages/score/score_page.dart index f19441cd..cf511602 100644 --- a/lib/ui/pages/score/score_page.dart +++ b/lib/ui/pages/score/score_page.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/config/app_colors.dart'; import 'package:flutter_app/src/model/course/course_score_json.dart'; -import 'package:flutter_app/src/model/course/course_syllabus_json.dart'; +import 'package:flutter_app/src/model/course/course_syllabus.dart'; import 'package:flutter_app/src/providers/app_provider.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; diff --git a/lib/ui/pages/videoplayer/class_video_player.dart b/lib/ui/pages/videoplayer/class_video_player.dart index d38ebe29..e268c3f4 100644 --- a/lib/ui/pages/videoplayer/class_video_player.dart +++ b/lib/ui/pages/videoplayer/class_video_player.dart @@ -8,7 +8,6 @@ import 'package:flutter_app/debug/log/log.dart'; import 'package:flutter_app/src/connector/core/connector.dart'; import 'package:flutter_app/src/connector/core/connector_parameter.dart'; import 'package:flutter_app/src/file/file_download.dart'; -import 'package:flutter_app/src/model/coursetable/course_table_json.dart'; import 'package:flutter_app/src/r.dart'; import 'package:flutter_app/src/store/local_storage.dart'; import 'package:flutter_app/src/util/language_util.dart'; @@ -20,16 +19,18 @@ import 'package:html/parser.dart'; import 'package:path/path.dart' as path; import 'package:video_player/video_player.dart'; +import '../../../src/model/coursetable/course.dart'; + class ClassVideoPlayer extends StatefulWidget { const ClassVideoPlayer( this.videoUrl, - this.courseInfo, + this.course, this.name, { super.key, }); final String videoUrl; - final CourseInfoJson courseInfo; + final Course course; final String name; @override @@ -192,7 +193,7 @@ class _VideoPlayer extends State { return; } - final courseName = widget.courseInfo.main.course.name; + final courseName = widget.course.name; final saveName = "${widget.name}_${_selectedVideoInfo.name}.mp4"; final subDir = (LanguageUtil.getLangIndex() == LangEnum.zh) ? "上課錄影" : "video"; final dirName = path.join(courseName, subDir); diff --git a/pubspec.lock b/pubspec.lock index 934aa762..b3361db7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -426,7 +426,7 @@ packages: source: hosted version: "2.1.17" equatable: - dependency: transitive + dependency: "direct main" description: name: equatable sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 diff --git a/pubspec.yaml b/pubspec.yaml index 801df6c1..98b7efc1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -91,6 +91,7 @@ dependencies: uuid: ^4.1.0 intl_utils: ^2.8.5 flutter_native_splash: ^2.2.19 # restricted by path 1.8.2 + equatable: ^2.0.5 dependency_overrides: