Skip to content

Commit

Permalink
Merge pull request #154 from NKUST-ITC/feature/inkust_helper
Browse files Browse the repository at this point in the history
更換課表爬蟲系統
  • Loading branch information
abc873693 authored Sep 14, 2020
2 parents 0cd2314 + 9ae6626 commit 4fb8931
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 2 deletions.
3 changes: 2 additions & 1 deletion lib/api/helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'package:nkust_ap/api/ap_helper.dart';
import 'package:nkust_ap/api/bus_helper.dart';
import 'package:nkust_ap/api/leave_helper.dart';
import 'package:nkust_ap/api/nkust_helper.dart';
import 'package:nkust_ap/api/inkust_helper.dart';
import 'package:nkust_ap/config/constants.dart';
import 'package:nkust_ap/models/booking_bus_data.dart';
import 'package:nkust_ap/models/bus_violation_records_data.dart';
Expand Down Expand Up @@ -352,7 +353,7 @@ class Helper {
}) async {
if (isExpire()) await login(username: username, password: password);
try {
var data = await WebApHelper.instance.coursetable(
var data = await InkustHelper.instance.courseTable(
semester.year,
semester.value,
);
Expand Down
114 changes: 114 additions & 0 deletions lib/api/inkust_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//dio
import 'package:ap_common/models/course_data.dart';
import 'package:dio/adapter.dart';
import 'package:dio/dio.dart';
import 'package:dio_http_cache/dio_http_cache.dart';
import 'package:cookie_jar/cookie_jar.dart';

//overwrite origin Cookie Manager.
import 'package:nkust_ap/api/private_cookie_manager.dart';
import "dart:math";
import 'helper.dart';
import 'package:nkust_ap/api/parser/inkust_parser.dart';

class InkustHelper {
static Dio dio;
static DioCacheManager _manager;
static InkustHelper _instance;
static CookieJar cookieJar;

static int reLoginReTryCountsLimit = 3;
static int reLoginReTryCounts = 0;
static String loginApiKey = "";
static String host = "inkusts.nkust.edu.tw";
static String get coursetableCacheKey =>
"${Helper.username}_coursetableCacheKey";

static Map<String, String> ueserRequestData = {
"apiKey": null,
"userId": null,
};

bool isLogin = false;

static InkustHelper get instance {
if (_instance == null) {
_instance = InkustHelper();
dioInit();
}
return _instance;
}

void setProxy(String proxyIP) {
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
(client) {
client.findProxy = (uri) {
return "PROXY " + proxyIP;
};
};
}

static dioInit() {
dio = Dio();
cookieJar = CookieJar();
if (Helper.isSupportCacheData) {
_manager = DioCacheManager(CacheConfig(baseUrl: "https://${host}"));
dio.interceptors.add(_manager.interceptor);
}

dio.interceptors.add(PrivateCookieManager(cookieJar));

var headerRandom = ['13_6', '12_4', '14_0', '13_1', '13_5'];
final _random = new Random();

dio.options.headers['user-agent'] =
'Mozilla/5.0 (iPhone; CPU iPhone OS ${headerRandom[_random.nextInt(headerRandom.length)]} like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148';
}

Future<Map<String, dynamic>> inkustLogin() async {
if (Helper.username == null || Helper.password == null) {
throw NullThrownError;
}
Response res = await dio.post("https://${host}/User/DoLogin2",
data: {
"apiKey": loginApiKey,
"userId": Helper.username,
"userPw": Helper.password,
"userKeep": 0
},
options: Options(contentType: Headers.formUrlEncodedContentType));

if (res.statusCode == 200 && res.data["success"] == true) {
isLogin = true;
ueserRequestData['apiKey'] = res.data['data']["userKey"];
ueserRequestData['userId'] = res.data['data']["userIdEncrypt"];
}
return res.data;
}

Future<CourseData> courseTable(String years, String semesterValue) async {
if (isLogin != true) {
await inkustLogin();
}
Options _options;
_options = Options(contentType: Headers.formUrlEncodedContentType);
if (Helper.isSupportCacheData) {
_options = buildConfigurableCacheOptions(
options: _options,
maxAge: Duration(hours: 1),
primaryKey: "${coursetableCacheKey}_${years}_${semesterValue}");
}

var requestData = new Map<String, String>.from(ueserRequestData);
requestData.addAll({
'academicYear': years,
'academicSms': semesterValue,
});
Response res = await dio.post("https://${host}/Course/GetStuCourse2",
data: requestData, options: _options);
if (res.data['success'] == false) {
return null;
}
return CourseData.fromJson(inkustCourseTableParser(res.data));
}
}
69 changes: 69 additions & 0 deletions lib/api/parser/inkust_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
Map<String, dynamic> inkustCourseTableParser(Map<String, dynamic> data) {
Map<String, dynamic> result = {
"courses": [],
"coursetable": {
"Monday": [],
"Tuesday": [],
'Wednesday': [],
'Thursday': [],
'Friday': [],
'Saturday': [],
'Sunday': [],
'timeCodes': [],
},
};
//reverse data type for more easy to use.
Map<String, dynamic> _tempDateTimeChange = {};

//timeCodes parse
data["data"]["time"].forEach((element) {
result["coursetable"]["timeCodes"].add("第${element["periodName"]}節");
_tempDateTimeChange.addAll({element["period"]: element});
});

//courses parse
data["data"]["course"].forEach((element) {
result["courses"].add({
"code": "",
"title": element['courseName'],
"className": element['className'],
"group": element["courseGroup"],
"units": element["courseCredit"],
"hours": element["courseHour"],
"required": element["courseOption"],
"at": element["courseAnnual"],
"times": element["courseTime"],
"location": {"room": element['courseRoom']},
"instructors": [element['courseTeacher']]
});
});

Map<String, String> courseWeek = {
"1": 'Monday',
"2": 'Tuesday',
"3": 'Wednesday',
"4": 'Thursday',
"5": 'Friday',
"6": 'Saturday',
"0": 'Sunday',
};
//coursetable parse
data["data"]["course"].forEach((courseElement) {
courseElement['courseTimeData'].forEach((singleCourseObject) {
result['coursetable'][courseWeek[singleCourseObject['courseWeek']]].add({
"title": courseElement['courseName'],
"date": {
"startTime":
"${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["begTime"].substring(0, 2)}:${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["begTime"].substring(2, 4)}",
"endTime":
"${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["endTime"].substring(0, 2)}:${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["endTime"].substring(2, 4)}",
"section":
"第${_tempDateTimeChange[singleCourseObject["coursePeriod"]]["periodName"]}節"
},
"location": {"room": courseElement['courseRoom']},
"instructors": [courseElement['courseTeacher']]
});
});
});
return result;
}
9 changes: 8 additions & 1 deletion lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'package:http/http.dart' as http;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:nkust_ap/api/ap_status_code.dart';
import 'package:nkust_ap/api/inkust_helper.dart';
import 'package:nkust_ap/models/login_response.dart';
import 'package:nkust_ap/models/models.dart';
import 'package:nkust_ap/pages/announcement/news_admin_page.dart';
Expand Down Expand Up @@ -656,13 +657,19 @@ class HomePageState extends State<HomePage> {
}
}

static const PREF_API_KEY = 'inkust_api_key';

void _checkFeatureEnable() async {
await Future.delayed(Duration(milliseconds: 100));
try {
final RemoteConfig remoteConfig = await RemoteConfig.instance;
await remoteConfig.fetch(expiration: const Duration(seconds: 10));
await remoteConfig.activateFetched();
busEnable = remoteConfig.getBool('bus_enable');
} catch (e) {}
InkustHelper.loginApiKey = remoteConfig.getString(PREF_API_KEY);
Preferences.setString(PREF_API_KEY, InkustHelper.loginApiKey);
} catch (e) {
InkustHelper.loginApiKey = Preferences.getString(PREF_API_KEY, '');
}
}
}

0 comments on commit 4fb8931

Please sign in to comment.