160 lines
5.8 KiB
Dart
160 lines
5.8 KiB
Dart
import 'dart:core';
|
||
import 'dart:convert';
|
||
import 'dart:developer';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:intl/intl.dart' show DateFormat;
|
||
|
||
import 'package:flutter/services.dart';
|
||
|
||
class ScheduleService {
|
||
static const Duration dayOffset = Duration(hours: 4);
|
||
|
||
Future<Map<String, dynamic>> getSchema() async {
|
||
final source = await rootBundle
|
||
.loadString('assets/schedule.json');
|
||
return jsonDecode(source);
|
||
}
|
||
|
||
Future<Map<String, dynamic>?> getRule(DateTime day) async {
|
||
final Map<String, dynamic> schema = await getSchema();
|
||
if(!schema.containsKey('rules')) return null;
|
||
|
||
for(final Map<String, dynamic> rule in (schema['rules'] as List<dynamic>)) {
|
||
final DateTime after = DateTime.parse(rule['after']);
|
||
final DateTime? before = rule['before'] != null
|
||
? DateTime.parse(rule['before'])
|
||
: null;
|
||
|
||
if(day.isAfter(after) && (before == null || day.isBefore(before)))
|
||
{ return rule; }
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
Future<Map<String, String>?> getDataset(DateTime day) async {
|
||
final Map<String, dynamic>? rule = await getRule(day);
|
||
|
||
if(rule == null || !rule.containsKey('dataset')) return null;
|
||
|
||
return (rule['dataset'] as Map<String, dynamic>).map((key, value) {
|
||
return MapEntry(key, value.toString());
|
||
});
|
||
}
|
||
|
||
String datetimeWeekdayToDatasetKey(int weekday) {
|
||
switch(weekday){
|
||
case DateTime.monday: return 'monday';
|
||
case DateTime.tuesday: return 'tuesday';
|
||
case DateTime.wednesday:return 'wednesday';
|
||
case DateTime.thursday: return 'thursday';
|
||
case DateTime.friday: return 'friday';
|
||
case DateTime.saturday: return 'saturday';
|
||
case DateTime.sunday: return 'sunday';
|
||
}
|
||
|
||
return 'monday';
|
||
}
|
||
|
||
Future<List<Map<String, dynamic>>?> getScheduleByKey(String key) async {
|
||
final Map<String, dynamic> schema = await getSchema();
|
||
if(!schema.containsKey('schedules')) return null;
|
||
if(!(schema['schedules'] as Map<String, dynamic>).containsKey(key)) return null;
|
||
|
||
return (schema['schedules'][key] as List<dynamic>).map((element) {
|
||
return element as Map<String, dynamic>;
|
||
}).toList();
|
||
}
|
||
|
||
Future<List<Map<String, dynamic>>?> getScheduleByDate(DateTime datetime, {bool force = false}) async {
|
||
final dateStart = datetime.copyWith(hour: 0, minute: 0, second: 0, microsecond: 0, millisecond: 0);
|
||
final dateEnd = datetime.copyWith(hour: 23, minute: 59, second: 59, microsecond: 999, millisecond: 999);
|
||
final datetimeStartWithOffset = dateStart.copyWith().add(dayOffset);
|
||
|
||
final DateTime usedScheduleDatetime = datetime.isAfter(datetimeStartWithOffset)
|
||
? datetime
|
||
: datetime.copyWith().subtract(const Duration(days: 1));
|
||
|
||
final Map<String, String>? dataset = await getDataset(usedScheduleDatetime);
|
||
if(dataset == null) return null;
|
||
|
||
final String dayKey = DateFormat('y-M-d').format(usedScheduleDatetime);
|
||
final String weekdayKey = datetimeWeekdayToDatasetKey(usedScheduleDatetime.weekday);
|
||
|
||
String? scheduleKey;
|
||
if(dataset.containsKey(dayKey)) { scheduleKey = dataset[dayKey]; }
|
||
else if(dataset.containsKey(weekdayKey)) { scheduleKey = dataset[weekdayKey]; }
|
||
|
||
if(scheduleKey == null) return null;
|
||
|
||
List<Map<String, dynamic>>? schedule = await getScheduleByKey(scheduleKey);
|
||
if(schedule == null) return null;
|
||
|
||
int durationOffset = 0;
|
||
for (var i = 0; i < schedule.length; i++) {
|
||
final scheduleItem = schedule[i];
|
||
final int duration = (scheduleItem['duration'] as int);
|
||
DateTime start = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset));
|
||
DateTime end = datetimeStartWithOffset.copyWith().add(Duration(minutes: durationOffset + duration));
|
||
durationOffset += duration;
|
||
|
||
if(!force) {
|
||
if(i == 0) {
|
||
final schedulePrevDay = await getScheduleByDate(datetime.copyWith().subtract(const Duration(days: 1)), force: true);
|
||
final lastScheduleElement = schedulePrevDay?.last;
|
||
if(lastScheduleElement != null && (lastScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) {
|
||
start = start.subtract(Duration(minutes: lastScheduleElement['duration'] as int));
|
||
}
|
||
}
|
||
|
||
if(i == (schedule.length -1)) {
|
||
final scheduleNextDay = await getScheduleByDate(datetime.copyWith().add(const Duration(days: 1)), force: true);
|
||
final firstScheduleElement = scheduleNextDay?.first;
|
||
if(firstScheduleElement != null && (firstScheduleElement['direction'] as String) == (scheduleItem['direction'] as String)) {
|
||
end = end.add(Duration(minutes: firstScheduleElement['duration'] as int));
|
||
}
|
||
}
|
||
}
|
||
|
||
schedule[i]['start'] = start;
|
||
schedule[i]['show_start_date'] = !(start.isAfter(dateStart) && start.isBefore(dateEnd));
|
||
schedule[i]['end'] = end;
|
||
schedule[i]['show_end_date'] = !(end.isAfter(dateStart) && end.isBefore(dateEnd));
|
||
}
|
||
|
||
return schedule;
|
||
}
|
||
|
||
Future<Map<String, dynamic>?> getCurrentStatus() async {
|
||
final DateTime now = DateTime.now();
|
||
final List<Map<String, dynamic>>? schedule = await getScheduleByDate(now);
|
||
if(schedule == null) return null;
|
||
|
||
for(var i = 0; i < schedule.length; i++) {
|
||
final start = schedule[i]['start'];
|
||
final end = schedule[i]['end'];
|
||
|
||
if(now.isAfter(start) && now.isBefore(end)) {
|
||
return schedule[i];
|
||
}
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
static String formatDirection(String direction) {
|
||
switch(direction) {
|
||
case 'in_city': return 'В город';
|
||
case 'out_city': return 'Из города';
|
||
default: return 'Переключение';
|
||
}
|
||
}
|
||
|
||
static IconData getIconByDirection(String? direction) {
|
||
switch(direction) {
|
||
case 'in_city': return Icons.input_outlined;
|
||
case 'out_city': return Icons.output_outlined;
|
||
default: return Icons.change_circle_outlined;
|
||
}
|
||
}
|
||
} |