Skip to content
This repository has been archived by the owner on Apr 25, 2022. It is now read-only.

Commit

Permalink
Merge pull request #128 from CCXXXI/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
CCXXXI committed Oct 23, 2021
2 parents d763aba + 848d835 commit 4a782d5
Show file tree
Hide file tree
Showing 14 changed files with 385 additions and 105 deletions.
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ linter:
avoid_escaping_inner_quotes: true
prefer_single_quotes: true
prefer_relative_imports: true
unnecessary_raw_strings: true
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
1 change: 1 addition & 0 deletions lib/settings/settings_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class SettingsLogic extends GetxController with L {
final v = version.split('.').map(int.parse);
for (final pair in zip([l, v])) {
if (pair[0] > pair[1]) return true;
if (pair[0] < pair[1]) return false;
}

return false;
Expand Down
5 changes: 3 additions & 2 deletions lib/settings/trivia.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ final _triviaStr = [
'师大校徽的标准颜色是`#a41f35`。'.s,
'全半角字符之间的空格被称为「盘古之白」。',
'横竖屏状态下有不同的布局方式。',
'由于某人不喜欢苹果严格的审核机制,此应用没有macOS或iOS版本。'.s,
'由于构建过程比较痛苦,此应用没有Linux版本。'.s,
'此应用的macOS和iOS版本未经签名且未经测试,因为某人不使用任何苹果设备。'.s,
'此应用为Windows平台同时提供了exe版本和msix版本。'.s,
'虽然一些字体会在用到时从Google Fonts下载,但考虑到部分用户的网络环境,常用字体文件直接打包进了应用。'.s,
'竖屏状态下,上方校训的字体固定为思源宋体,不会受字体设置影响。',
'最新版本信息需要从GitHub获取,在某些网络环境下可能会失败。'.s,
Expand All @@ -32,6 +32,7 @@ final _triviaStr = [
'字体与图标颜色会随背景色自动调整。',
'横屏状态下,点击校徽可以展开/收起侧栏。'.s,
'校历图片的放大倍数上限是`double.infinity`。'.s,
'此应用基于Flutter的master渠道构建,这保证了最新特性可用,同时也带来了一些不稳定性。'.s,
];

final trivia =
Expand Down
127 changes: 84 additions & 43 deletions lib/timetable/ecnu/ecnu_logic.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,6 @@ class EcnuLogic extends GetxController with L {
final passwordController = TextEditingController(text: user.password);
final captchaController = TextEditingController();

final checkFormKey = GlobalKey<FormState>();

// todo: controllers

@override
void onClose() {
super.onClose();
Expand All @@ -54,12 +50,11 @@ class EcnuLogic extends GetxController with L {
step.value == S.login && !loginFormKey.currentState!.validate()) return;

if (step.value == S.login) {
isLoading.value = true;
try {
final loginResult = await login();
if (loginResult == null) {
step.value = S.check;
getTable();
getTimetable();
} else {
Get.back();
Get.snackbar('登录失败', loginResult);
Expand All @@ -71,9 +66,8 @@ class EcnuLogic extends GetxController with L {
}
} else {
Get.back();
Get.back();
}

isLoading.value = false;
}

@override
Expand All @@ -88,6 +82,7 @@ class EcnuLogic extends GetxController with L {
void initEcnu() async {
try {
// set cookie
await cookieJar.deleteAll();
await dio.get(Url.portal);
l.debug(await cookieJar.loadForRequest(Uri.parse(Url.portal)));

Expand Down Expand Up @@ -134,6 +129,8 @@ class EcnuLogic extends GetxController with L {

/// Returns null if success.
Future<String?> login() async {
isLoading.value = true;

final id = idController.text;
final password = passwordController.text;
final captcha = captchaController.text;
Expand Down Expand Up @@ -194,47 +191,91 @@ class EcnuLogic extends GetxController with L {
return '未知错误';
}

final table = ''.obs;
final year = guessYear(DateTime.now()).obs;
final semester = guessSemester(DateTime.now()).obs;
final coursesPreview = ''.obs;

static int guessYear(DateTime date) =>
date.month >= 8 ? date.year : date.year - 1;

static int guessSemester(DateTime date) => date.month >= 8 || date.month <= 1
? 0
: date.month == 7
? 2
: 1;

Map<int, String> get years => {
for (var y = year.value - 2; y <= year.value + 2; y++)
y: '$y - ${y + 1}',
};

final semesters = {
0: '第一学期',
1: '第二学期',
2: '暑期学期',
};

void yearOnChanged(int value) {
year.value = value;
getTimetable();
}

void getTable() async {
final r0 = await dio.get(Url.ids);
final ids = RegExp(r'bg\.form\.addInput\(form,"ids","(\d+)"\);')
.firstMatch(r0.data)!
.group(1)!;
void semesterOnChanged(int value) {
semester.value = value;
getTimetable();
}

final r1 = await dio.post(
Url.table,
data: {
// todo: let user determines these options
'ignoreHead': 1,
'setting.kind': 'std',
'startWeek': 1,
'semester.id': semId(2021, 0),
'ids': ids,
},
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
final document = parseHtmlDocument(r1.data);
final js = document.querySelectorAll('script[language]').last.text;
courses
..clear()
..addAll(parseJs(js!));
table.value = courses.toMap().toString();
void getTimetable() async {
isLoading.value = true;

try {
final r0 = await dio.get(Url.ids);
final ids = getIds(r0.data);

final r1 = await dio.post(
Url.table,
data: {
'ignoreHead': 1,
'setting.kind': 'std',
'startWeek': 1,
'semester.id': semesterId(year.value, semester.value),
'ids': ids,
},
options: Options(
contentType: Headers.formUrlEncodedContentType,
),
);
final document = parseHtmlDocument(r1.data);
final coursesJs = document.querySelectorAll('script[language]').last.text;

await courses.clear();
await courses.addAll(getCourses(coursesJs!));
coursesPreview.value =
courses.values.map((e) => e.courseName).toSet().join('\n');
} catch (e) {
coursesPreview.value = '获取失败';
}

isLoading.value = false;
}

static String getIds(String data) =>
RegExp(r'bg\.form\.addInput\(form,"ids","(.*)"\);')
.firstMatch(data)!
.group(1)!;

/// 2018-2019学年度上学期为705,每向前/向后一个学期就增加/减少32
static int semId(int year, int sem) => 705 + (year - 2018) * 96 + sem * 32;
static int semesterId(int year, int semester) =>
705 + (year - 2018) * 96 + semester * 32;

static List<Course> parseJs(String js) {
static List<Course> getCourses(String coursesJs) {
final newCourse = RegExp('TaskActivity'
r'\('
'"(?<teacherId>.*)",'
'"(?<teacherName>.*)",'
r'"(?<courseId>\d*)\((?<courseCode>.*)\.(?<courseNo>.*)\)",'
r'"(?<courseId>.*)\((?<courseCode>.*)\.(?<courseNo>.*)\)",'
r'"(?<courseName>.*)\((?<courseCode2>.*)\.(?<courseNo2>.*)\)",'
r'"(?<roomId>\d*)",'
'"(?<roomId>.*)",'
'"(?<roomName>.*)",'
'"(?<weeks>[01]{53})",'
'(?<taskId>null),'
Expand All @@ -247,7 +288,7 @@ class EcnuLogic extends GetxController with L {

final courseBuffer = <Course>[];

for (final line in js.split(';')) {
for (final line in coursesJs.split(';')) {
final n = newCourse.firstMatch(line);
if (n != null) {
assert(n.namedGroup('courseCode') == n.namedGroup('courseCode2'));
Expand All @@ -257,11 +298,11 @@ class EcnuLogic extends GetxController with L {
Course()
..teacherId = n.namedGroup('teacherId')
..teacherName = n.namedGroup('teacherName')
..courseId = int.tryParse(n.namedGroup('courseId') ?? '')
..courseId = n.namedGroup('courseId')
..courseCode = n.namedGroup('courseCode')
..courseNo = n.namedGroup('courseNo')
..courseName = n.namedGroup('courseName')
..roomId = int.tryParse(n.namedGroup('roomId') ?? '')
..roomId = n.namedGroup('roomId')
..roomName = n.namedGroup('roomName')
..weeks = n
.namedGroup('weeks')
Expand All @@ -283,8 +324,8 @@ class EcnuLogic extends GetxController with L {
if (p != null) {
courseBuffer.last.periods!.add(
Period()
..weekday = int.tryParse(p.namedGroup('weekday') ?? '')
..unit = int.tryParse(p.namedGroup('unit') ?? ''),
..weekday = int.parse(p.namedGroup('weekday')!)
..unit = int.parse(p.namedGroup('unit')!),
);
}
}
Expand Down
92 changes: 60 additions & 32 deletions lib/timetable/ecnu/ecnu_view.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_settings_screens/flutter_settings_screens.dart';
import 'package:get/get.dart';

import '../../utils/loading.dart';
Expand All @@ -21,7 +22,12 @@ class EcnuPage extends StatelessWidget {
() => Stepper(
currentStep: logic.step.value.index,
onStepContinue: logic.onStepContinue,
controlsBuilder: controlsBuilder,
controlsBuilder: logic.isLoading.isTrue
? (_, __) => Loading()
: (_, details) => ElevatedButton(
onPressed: details.onStepContinue,
child: Text(['登录', '完成'][details.stepIndex]),
),
steps: [
Step(
title: const Text('登录公共数据库'),
Expand Down Expand Up @@ -57,29 +63,25 @@ class EcnuPage extends StatelessWidget {
Row(
children: [
Expanded(
child: Obx(
() => TextFormField(
decoration: const InputDecoration(
label: Text('验证码'),
),
controller: logic.captchaController,
validator: EcnuLogic.captchaValidator,
maxLength: 4,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
keyboardType: TextInputType.number,
onEditingComplete: logic.onStepContinue,
enabled: logic.captchaReady.value,
child: TextFormField(
decoration: const InputDecoration(
label: Text('验证码'),
),
controller: logic.captchaController,
validator: EcnuLogic.captchaValidator,
maxLength: 4,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
],
keyboardType: TextInputType.number,
onEditingComplete: logic.onStepContinue,
enabled: logic.captchaReady.value,
),
),
const SizedBox(width: 42),
Obx(
() => logic.captchaImage.value.isEmpty
? Loading()
: Image.memory(logic.captchaImage.value),
),
logic.captchaImage.value.isEmpty
? Loading()
: Image.memory(logic.captchaImage.value),
],
),
],
Expand All @@ -90,9 +92,44 @@ class EcnuPage extends StatelessWidget {
: StepState.disabled,
),
Step(
title: const Text('确认课表内容'),
subtitle: Text('有误可至GitHub反馈。'.s),
content: Text(logic.table.value),
title: const Text('选择学年学期'),
subtitle: const Text('并确认课表内容无误。'),
content: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: 200,
child: DropDownSettingsTile(
title: '学年',
settingKey: 'timetable.year.$hashCode',
selected: logic.year.value,
values: logic.years,
enabled: logic.isLoading.isFalse,
onChange: logic.yearOnChanged,
),
),
SizedBox(
width: 200,
child: DropDownSettingsTile(
title: '学期',
settingKey: 'timetable.semester.$hashCode',
selected: logic.semester.value,
values: logic.semesters,
enabled: logic.isLoading.isFalse,
onChange: logic.semesterOnChanged,
),
),
],
),
Text(
logic.coursesPreview.value,
textAlign: TextAlign.center,
),
const Divider(),
],
),
state: logic.step.value == S.check
? StepState.indexed
: StepState.disabled,
Expand All @@ -102,13 +139,4 @@ class EcnuPage extends StatelessWidget {
),
);
}

Widget controlsBuilder(BuildContext context, ControlsDetails details) => Obx(
() => logic.isLoading.isTrue
? Loading()
: ElevatedButton(
onPressed: details.onStepContinue,
child: Text(['登录', '完成'][details.stepIndex]),
),
);
}
Loading

0 comments on commit 4a782d5

Please sign in to comment.