changed everything from filcnaplo to refilc finally

This commit is contained in:
Kima
2024-02-24 20:12:25 +01:00
parent 0d1c7b7143
commit 1171e3aaaf
655 changed files with 38728 additions and 44967 deletions

View File

@@ -0,0 +1,145 @@
import 'package:intl/intl.dart';
class KretaAPI {
// IDP API
static const login = BaseKreta.kretaIdp + KretaApiEndpoints.token;
static const logout = BaseKreta.kretaIdp + KretaApiEndpoints.revoke;
static const nonce = BaseKreta.kretaIdp + KretaApiEndpoints.nonce;
static const clientId = "kreta-ellenorzo-mobile-android";
// ELLENORZO API
static String notes(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.notes;
static String events(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.events;
static String student(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.student;
static String grades(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.grades;
static String absences(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.absences;
static String groups(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.groups;
static String groupAverages(String iss, String uid) =>
"${BaseKreta.kreta(iss)}${KretaApiEndpoints.groupAverages}?oktatasiNevelesiFeladatUid=$uid";
static String averages(String iss, String uid) =>
"${BaseKreta.kreta(iss)}${KretaApiEndpoints.averages}?oktatasiNevelesiFeladatUid=$uid";
static String timetable(String iss, {DateTime? start, DateTime? end}) =>
BaseKreta.kreta(iss) +
KretaApiEndpoints.timetable +
(start != null && end != null
? "?datumTol=${start.toUtc().toIso8601String()}&datumIg=${end.toUtc().toIso8601String()}"
: "");
static String exams(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.exams;
static String homework(String iss, {DateTime? start, String? id}) =>
BaseKreta.kreta(iss) +
KretaApiEndpoints.homework +
(id != null ? "/$id" : "") +
(id == null && start != null
? "?datumTol=${DateFormat('yyyy-MM-dd').format(start)}"
: "");
static String capabilities(String iss) =>
BaseKreta.kreta(iss) + KretaApiEndpoints.capabilities;
static String downloadHomeworkAttachments(
String iss, String uid, String type) =>
BaseKreta.kreta(iss) +
KretaApiEndpoints.downloadHomeworkAttachments(uid, type);
static String subjects(String iss, String uid) =>
"${BaseKreta.kreta(iss)}${KretaApiEndpoints.subjects}?oktatasiNevelesiFeladatUid=$uid";
// Structure:
// {
// "Uid": 000,
// "Tantargy": {
// "Uid": 000,
// "Nev": "Irodalom",
// "Kategoria": {
// "Uid": "000,magyar_nyelv_es_irodalom",
// "Nev": "magyar_nyelv_es_irodalom",
// "Leiras": "Magyar nyelv és irodalom"
// },
// "SortIndex": 0,
// },
// "Atlag": null, // float
// "AtlagAlakulasaIdoFuggvenyeben": Array[], // no idea what this is
// "SulyozottOsztalyzatOsszege": null, // int | float
// "SulyozottOsztalyzatSzama": null, // int | float
// "SortIndex": 0
// }
// refer to https://discord.com/channels/1111649116020285532/1111798771513303102/1148368925969612920
// ADMIN API
static const sendMessage =
BaseKreta.kretaAdmin + KretaAdminEndpoints.sendMessage;
static String messages(String endpoint) =>
BaseKreta.kretaAdmin + KretaAdminEndpoints.messages(endpoint);
static String message(String id) =>
BaseKreta.kretaAdmin + KretaAdminEndpoints.message(id);
static const recipientCategories =
BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientCategories;
static const availableCategories =
BaseKreta.kretaAdmin + KretaAdminEndpoints.availableCategories;
static const recipientTeachers =
BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientTeachers;
static const recipientDirectorate =
BaseKreta.kretaAdmin + KretaAdminEndpoints.recipientDirectorate;
static const uploadAttachment =
BaseKreta.kretaAdmin + KretaAdminEndpoints.uploadAttachment;
static String downloadAttachment(String id) =>
BaseKreta.kretaAdmin + KretaAdminEndpoints.downloadAttachment(id);
static const trashMessage =
BaseKreta.kretaAdmin + KretaAdminEndpoints.trashMessage;
static const deleteMessage =
BaseKreta.kretaAdmin + KretaAdminEndpoints.deleteMessage;
}
class BaseKreta {
static String kreta(String iss) => "https://$iss.e-kreta.hu";
static const kretaIdp = "https://idp.e-kreta.hu";
static const kretaAdmin = "https://eugyintezes.e-kreta.hu";
static const kretaFiles = "https://files.e-kreta.hu";
}
class KretaApiEndpoints {
static const token = "/connect/token";
static const revoke = "/connect/revocation";
static const nonce = "/nonce";
static const notes = "/ellenorzo/V3/Sajat/Feljegyzesek";
static const events = "/ellenorzo/V3/Sajat/FaliujsagElemek";
static const student = "/ellenorzo/V3/Sajat/TanuloAdatlap";
static const grades = "/ellenorzo/V3/Sajat/Ertekelesek";
static const absences = "/ellenorzo/V3/Sajat/Mulasztasok";
static const groups = "/ellenorzo/V3/Sajat/OsztalyCsoportok";
static const groupAverages =
"/ellenorzo/V3/Sajat/Ertekelesek/Atlagok/OsztalyAtlagok";
static const averages = "/ellenorzo/V3/idk";
static const timetable = "/ellenorzo/V3/Sajat/OrarendElemek";
static const exams = "/ellenorzo/V3/Sajat/BejelentettSzamonkeresek";
static const homework = "/ellenorzo/V3/Sajat/HaziFeladatok";
// static const homeworkDone = "/ellenorzo/V3/Sajat/HaziFeladatok/Megoldva"; // Removed from the API
static const capabilities = "/ellenorzo/V3/Sajat/Intezmenyek";
static String downloadHomeworkAttachments(String uid, String type) =>
"/ellenorzo/V3/Sajat/Csatolmany/$uid";
static const subjects =
"/ellenorzo/V3/Sajat/Ertekelesek/Atlagok/TantargyiAtlagok";
}
class KretaAdminEndpoints {
//static const messages = "/api/v1/kommunikacio/postaladaelemek/sajat";
static const sendMessage = "/api/v1/kommunikacio/uzenetek";
static String messages(String endpoint) =>
"/api/v1/kommunikacio/postaladaelemek/$endpoint";
static String message(String id) =>
"/api/v1/kommunikacio/postaladaelemek/$id";
static const recipientCategories = "/api/v1/adatszotarak/cimzetttipusok";
static const availableCategories = "/api/v1/kommunikacio/cimezhetotipusok";
static const recipientTeachers = "/api/v1/kreta/alkalmazottak/tanar";
static const recipientDirectorate = "/api/v1/kreta/alkalmazottak/igazgatosag";
static const uploadAttachment = "/ideiglenesfajlok";
static String downloadAttachment(String id) =>
"/api/v1/dokumentumok/uzenetek/$id";
static const trashMessage = "/api/v1/kommunikacio/postaladaelemek/kuka";
static const deleteMessage = "/api/v1/kommunikacio/postaladaelemek/torles";
// profile management
static const editProfile = "/api/profilapi/saveprofildata";
}

View File

@@ -0,0 +1,313 @@
// ignore_for_file: avoid_print
import 'dart:convert';
import 'dart:io';
import 'package:refilc/api/login.dart';
import 'package:refilc/api/nonce.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/status_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc/utils/jwt.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;
import 'dart:async';
class KretaClient {
String? accessToken;
String? refreshToken;
String? idToken;
String? userAgent;
late http.Client client;
late final SettingsProvider _settings;
late final UserProvider _user;
late final StatusProvider _status;
bool _loginRefreshing = false;
KretaClient({
this.accessToken,
required SettingsProvider settings,
required UserProvider user,
required StatusProvider status,
}) : _settings = settings,
_user = user,
_status = status,
userAgent = settings.config.userAgent {
var ioclient = HttpClient();
ioclient.badCertificateCallback = _checkCerts;
client = http.IOClient(ioclient);
}
bool _checkCerts(X509Certificate cert, String host, int port) {
return _settings.developerMode;
}
Future<dynamic> getAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
bool json = true,
bool rawResponse = false,
}) async {
Map<String, String> headerMap;
if (rawResponse) json = false;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
try {
http.Response? res;
for (int i = 0; i < 3; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
}
res = await client.get(Uri.parse(url), headers: headerMap);
_status.triggerRequest(res);
if (res.statusCode == 401) {
await refreshLogin();
headerMap.remove("authorization");
} else {
break;
}
// Wait before retrying
await Future.delayed(const Duration(milliseconds: 500));
}
if (res == null) throw "Login error";
if (res.body == 'invalid_grant' || res.body.replaceAll(' ', '') == '') {
throw "Auth error";
}
if (json) {
return jsonDecode(res.body);
} else if (rawResponse) {
return res.bodyBytes;
} else {
return res.body;
}
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.getAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.getAPI ($url) ${error.runtimeType}: $error");
}
}
Future<dynamic> postAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
bool json = true,
Object? body,
}) async {
Map<String, String> headerMap;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
try {
http.Response? res;
for (int i = 0; i < 3; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
if (!headerMap.containsKey("content-type")) {
headerMap["content-type"] = "application/json";
}
if (url.contains('kommunikacio/uzenetek')) {
headerMap["X-Uzenet-Lokalizacio"] = "hu-HU";
}
}
res = await client.post(Uri.parse(url), headers: headerMap, body: body);
if (res.statusCode == 401) {
await refreshLogin();
headerMap.remove("authorization");
} else {
break;
}
}
if (res == null) throw "Login error";
if (json) {
print(jsonDecode(res.body));
return jsonDecode(res.body);
} else {
return res.body;
}
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.postAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.postAPI ($url) ${error.runtimeType}: $error");
}
}
Future<dynamic> sendFilesAPI(
String url, {
Map<String, String>? headers,
bool autoHeader = true,
Map<String, String>? body,
}) async {
Map<String, String> headerMap;
if (headers != null) {
headerMap = headers;
} else {
headerMap = {};
}
try {
http.StreamedResponse? res;
for (int i = 0; i < 3; i++) {
if (autoHeader) {
if (!headerMap.containsKey("authorization") && accessToken != null) {
headerMap["authorization"] = "Bearer $accessToken";
}
if (!headerMap.containsKey("user-agent") && userAgent != null) {
headerMap["user-agent"] = "$userAgent";
}
if (!headerMap.containsKey("content-type")) {
headerMap["content-type"] = "multipart/form-data";
}
if (url.contains('kommunikacio/uzenetek')) {
headerMap["X-Uzenet-Lokalizacio"] = "hu-HU";
}
}
var request = http.MultipartRequest("POST", Uri.parse(url));
// request.files.add(value)
request.fields.addAll(body ?? {});
request.headers.addAll(headers ?? {});
res = await request.send();
if (res.statusCode == 401) {
await refreshLogin();
headerMap.remove("authorization");
} else {
break;
}
}
if (res == null) throw "Login error";
print(res.statusCode);
return res.statusCode;
} on http.ClientException catch (error) {
print(
"ERROR: KretaClient.postAPI ($url) ClientException: ${error.message}");
} catch (error) {
print("ERROR: KretaClient.postAPI ($url) ${error.runtimeType}: $error");
}
}
Future<void> refreshLogin() async {
if (_loginRefreshing) return;
_loginRefreshing = true;
User? loginUser = _user.user;
if (loginUser == null) return;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
String nonceStr = await getAPI(KretaAPI.nonce, json: false);
Nonce nonce =
getNonce(nonceStr, loginUser.username, loginUser.instituteCode);
headers.addAll(nonce.header());
if (_settings.presentationMode) {
print("DEBUG: refreshLogin: ${loginUser.id}");
} else {
print("DEBUG: refreshLogin: ${loginUser.id} ${loginUser.name}");
}
Map? loginRes = await postAPI(
KretaAPI.login,
headers: headers,
body: User.loginBody(
username: loginUser.username,
password: loginUser.password,
instituteCode: loginUser.instituteCode,
),
);
if (loginRes != null) {
if (loginRes.containsKey("access_token")) {
accessToken = loginRes["access_token"];
}
if (loginRes.containsKey("refresh_token")) {
refreshToken = loginRes["refresh_token"];
}
// Update role
loginUser.role =
JwtUtils.getRoleFromJWT(accessToken ?? "") ?? Role.student;
}
if (refreshToken != null) {
Map? refreshRes = await postAPI(KretaAPI.login,
headers: headers,
body: User.refreshBody(
refreshToken: refreshToken!,
instituteCode: loginUser.instituteCode));
if (refreshRes != null) {
if (refreshRes.containsKey("id_token")) {
idToken = refreshRes["id_token"];
}
}
}
_loginRefreshing = false;
}
Future<void> logout() async {
User? loginUser = _user.user;
if (loginUser == null) return;
Map<String, String> headers = {
"content-type": "application/x-www-form-urlencoded",
};
await postAPI(
KretaAPI.logout,
headers: headers,
body: User.logoutBody(
refreshToken: refreshToken!,
),
json: false,
);
}
}

View File

@@ -0,0 +1,5 @@
import 'package:flutter/foundation.dart';
class ProfileController extends ChangeNotifier {
//todo: profile controller (pw change, etc)
}

View File

@@ -0,0 +1,241 @@
// ignore_for_file: avoid_print, use_build_context_synchronously
import 'dart:async';
import 'dart:developer';
import 'dart:math' as math;
import 'package:refilc_kreta_api/providers/homework_provider.dart';
import 'package:refilc_kreta_api/providers/timetable_provider.dart';
import 'package:refilc_kreta_api/models/lesson.dart';
import 'package:refilc_kreta_api/models/week.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
enum LoadType { initial, offline, loading, online }
class TimetableController extends ChangeNotifier {
late Week currentWeek;
int currentWeekId = -1;
late int previousWeekId;
List<List<Lesson>>? days;
LoadType loadType = LoadType.initial;
TimetableController() {
current();
}
static int getWeekId(Week week) =>
(week.start.difference(getSchoolYearStart()).inDays /
DateTime.daysPerWeek)
.ceil();
static DateTime getSchoolYearStart() {
DateTime now = DateTime.now();
DateTime nowStart = _getYearStart(now.year);
if (nowStart.isBefore(now)) {
return nowStart;
} else {
return _getYearStart(now.year - 1);
}
}
static DateTime _getYearStart(int year) {
var s1 = DateTime(year, DateTime.september, 1);
if (s1.weekday == 6) {
s1.add(const Duration(days: 2));
} else if (s1.weekday == 7) {
s1.add(const Duration(days: 1));
}
return s1;
}
// Jump shortcuts
Future<void> next(BuildContext context) =>
jump(Week.fromId(currentWeekId + 1), context: context);
Future<void> previous(BuildContext context) =>
jump(Week.fromId(currentWeekId - 1), context: context);
void current() {
Week week = Week.current();
int id = getWeekId(week);
if (id > 51) id = 51;
if (id < 0) id = 0;
_setWeek(Week.fromId(id));
}
Future<void> jump(Week week,
{required BuildContext context,
bool initial = false,
bool skip = false,
bool loader = true}) async {
if (_setWeek(week)) return;
loadType = LoadType.initial;
if (loader) {
days = null;
// Don't start loading on init
if (!initial) notifyListeners();
}
days = _sortDays(week, context: context);
if (week != currentWeek) return;
loadType = LoadType.loading;
notifyListeners();
try {
await _fetchWeek(week, context: context).timeout(
const Duration(seconds: 5),
onTimeout: (() => throw "timeout"));
loadType = LoadType.online;
} catch (error, stack) {
print("ERROR: TimetableController.jump: $error\n$stack");
loadType = LoadType.offline;
}
if (week != currentWeek) return;
days = _sortDays(week, context: context);
if (week != currentWeek) return;
// Jump to next week on weekends
if (skip &&
(days?.length ?? 0) > 0 &&
days!.last.last.end.isBefore(DateTime.now())) return next(context);
notifyListeners();
}
bool _setWeek(Week week) {
int id = getWeekId(week);
if (id > 51) return true; // Max 52.
if (id < 0) return true; // Min 1.
// Set week start to Sept. 1 of first week
if (!_differentDate(week.start, Week.fromId(0).start)) {
week.start = TimetableController.getSchoolYearStart();
}
currentWeek = week;
previousWeekId = currentWeekId;
currentWeekId = id;
return false;
}
Future<void> _fetchWeek(Week week, {required BuildContext context}) async {
await Future.wait([
context.read<TimetableProvider>().fetch(week: week),
context.read<HomeworkProvider>().fetch(from: week.start, db: false),
]);
}
List<List<Lesson>> _sortDays(Week week, {required BuildContext context}) {
List<List<Lesson>> days = [];
try {
final timetableProvider = context.read<TimetableProvider>();
List<Lesson> lessons = timetableProvider.getWeek(week) ?? [];
if (lessons.isNotEmpty) {
days.add([]);
lessons.sort((a, b) => a.date.compareTo(b.date));
for (var lesson in lessons) {
if (days.last.isNotEmpty &&
_differentDate(lesson.date, days.last.last.date)) days.add([]);
days.last.add(lesson);
}
for (int i = 0; i < days.length; i++) {
List<Lesson> day0 = List.castFrom(days[i]);
List<int> lessonIndexes = _getIndexes(day0);
int minIndex = 0, maxIndex = 0;
if (lessonIndexes.isNotEmpty) {
minIndex = lessonIndexes.reduce(math.min);
maxIndex = lessonIndexes.reduce(math.max);
}
List<Lesson> day = [];
if (lessonIndexes.isNotEmpty) {
// Fill missing indexes with empty spaces
for (var i in List<int>.generate(
maxIndex - minIndex + 1, (int i) => minIndex + i)) {
List<Lesson> indexLessons = _getLessonsByIndex(day0, i);
// Empty lesson
if (indexLessons.isEmpty) {
// Get start date by previous lesson
List<Lesson> prevLesson = _getLessonsByIndex(day, i - 1);
try {
DateTime startDate =
prevLesson.last.start.add(const Duration(seconds: 1));
indexLessons.add(Lesson.fromJson({
'isEmpty': true,
'Oraszam': i,
'KezdetIdopont': startDate.toIso8601String()
}));
// ignore: empty_catches
} catch (e) {}
}
day.addAll(indexLessons);
}
}
// Additional lessons
day.addAll(day0.where((l) =>
int.tryParse(l.lessonIndex) == null && l.subject.id != ''));
day.sort((a, b) => a.start.compareTo(b.start));
// Special Dates
for (var l in day0) {
l.subject.id == '' ? day.insert(0, l) : null;
}
days[i] = day;
}
}
} catch (e) {
log("_sortDays error: $e");
}
return days;
}
List<Lesson> _getLessonsByIndex(List<Lesson> lessons, int index) {
List<Lesson> ret = [];
for (var lesson in lessons) {
int? lessonIndex = int.tryParse(lesson.lessonIndex);
if (lessonIndex != null && lessonIndex == index) {
ret.add(lesson);
}
}
return ret;
}
List<int> _getIndexes(List<Lesson> lessons) {
List<int> indexes = [];
for (var l in lessons) {
int? index = int.tryParse(l.lessonIndex);
if (index != null) indexes.add(index);
}
return indexes;
}
bool _differentDate(DateTime a, DateTime b) =>
!(a.year == b.year && a.month == b.month && a.day == b.day);
}

View File

@@ -0,0 +1,98 @@
import "category.dart";
import "subject.dart";
import "teacher.dart";
class Absence {
Map? json;
String id;
DateTime date;
int delay;
DateTime submitDate;
Teacher teacher;
Justification state;
Category? justification;
Category? type;
Category? mode;
GradeSubject subject;
DateTime lessonStart;
DateTime lessonEnd;
int? lessonIndex;
String group;
bool isSeen;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Absence && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
Absence({
required this.id,
required this.date,
required this.delay,
required this.submitDate,
required this.teacher,
required this.state,
this.justification,
this.type,
this.mode,
required this.subject,
required this.lessonStart,
required this.lessonEnd,
this.lessonIndex,
required this.group,
this.json,
this.isSeen = false,
});
factory Absence.fromJson(Map json) {
DateTime lessonStart;
DateTime lessonEnd;
int? lessonIndex;
if (json["Ora"] != null) {
lessonStart = json["Ora"]["KezdoDatum"] != null
? DateTime.parse(json["Ora"]["KezdoDatum"]).toLocal()
: DateTime(0);
lessonEnd = json["Ora"]["VegDatum"] != null
? DateTime.parse(json["Ora"]["VegDatum"]).toLocal()
: DateTime(0);
lessonIndex = json["Ora"]["Oraszam"];
} else {
lessonStart = DateTime(0);
lessonEnd = DateTime(0);
}
return Absence(
id: json["Uid"],
date: json["Datum"] != null
? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0),
delay: json["KesesPercben"] ?? 0,
submitDate: json["KeszitesDatuma"] != null
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
: DateTime(0),
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
state: json["IgazolasAllapota"] == "Igazolt"
? Justification.excused
: json["IgazolasAllapota"] == "Igazolando"
? Justification.pending
: Justification.unexcused,
justification: json["IgazolasTipusa"] != null
? Category.fromJson(json["IgazolasTipusa"])
: null,
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
mode: json["Mod"] != null ? Category.fromJson(json["Mod"]) : null,
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
lessonStart: lessonStart,
lessonEnd: lessonEnd,
lessonIndex: lessonIndex,
group:
json["OsztalyCsoport"] != null ? json["OsztalyCsoport"]["Uid"] : "",
isSeen: false,
json: json,
);
}
}
enum Justification { excused, unexcused, pending }

View File

@@ -0,0 +1,33 @@
import 'package:file_picker/file_picker.dart';
import 'package:refilc_kreta_api/client/api.dart';
class Attachment {
Map? json;
int id;
PlatformFile? file;
String name;
String? fileId;
String kretaFilePath;
Attachment({
required this.id,
this.file,
required this.name,
this.fileId,
required this.kretaFilePath,
this.json,
});
factory Attachment.fromJson(Map json) {
return Attachment(
id: json["azonosito"],
name: (json["fajlNev"] ?? "attachment").trim(),
kretaFilePath: json["utvonal"] ?? "",
json: json,
);
}
String get downloadUrl => KretaAPI.downloadAttachment(id.toString());
bool get isImage =>
name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png");
}

View File

@@ -0,0 +1,44 @@
import 'grade.dart';
class Category {
String id;
String description;
String name;
Category({
required this.id,
this.description = "",
this.name = "",
});
factory Category.fromJson(Map json) {
return Category(
id: json["Uid"] ?? "",
description: json["Leiras"] != "Na" ? json["Leiras"] ?? "" : "",
name: json["Nev"] != "Na" ? json["Nev"] ?? "" : "",
);
}
static GradeType getGradeType(String string) {
switch (string) {
case "evkozi_jegy_ertekeles":
return GradeType.midYear;
case "I_ne_jegy_ertekeles":
return GradeType.firstQ;
case "II_ne_jegy_ertekeles":
return GradeType.secondQ;
case "felevi_jegy_ertekeles":
return GradeType.halfYear;
case "III_ne_jegy_ertekeles":
return GradeType.thirdQ;
case "IV_ne_jegy_ertekeles":
return GradeType.fourthQ;
case "evvegi_jegy_ertekeles":
return GradeType.endYear;
case "osztalyozo_vizsga":
return GradeType.levelExam;
default:
return GradeType.unknown;
}
}
}

View File

@@ -0,0 +1,28 @@
class Event {
Map? json;
String id;
DateTime start;
DateTime end;
String title;
String content;
Event({
required this.id,
required this.start,
required this.end,
this.title = "",
this.content = "",
this.json,
});
factory Event.fromJson(Map json) {
return Event(
id: json["Uid"] ?? "",
start: json["ErvenyessegKezdete"] != null ? DateTime.parse(json["ErvenyessegKezdete"]).toLocal() : DateTime(0),
end: json["ErvenyessegVege"] != null ? DateTime.parse(json["ErvenyessegVege"]).toLocal() : DateTime(0),
title: json["Cim"] ?? "",
content: json["Tartalom"] != null ? json["Tartalom"].replaceAll("\r", "") : "",
json: json,
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:refilc_kreta_api/models/subject.dart';
import 'category.dart';
import 'teacher.dart';
class Exam {
Map? json;
DateTime date;
DateTime writeDate;
Category? mode;
// int? subjectIndex;
// String subjectName;
GradeSubject subject;
Teacher teacher;
String description;
String group;
String id;
Exam({
required this.id,
required this.date,
required this.writeDate,
this.mode,
// this.subjectIndex,
// required this.subjectName,
required this.subject,
required this.teacher,
required this.description,
required this.group,
this.json,
});
factory Exam.fromJson(Map json) {
return Exam(
id: json["Uid"] ?? "",
date: json["BejelentesDatuma"] != null
? DateTime.parse(json["BejelentesDatuma"]).toLocal()
: DateTime(0),
writeDate: json["Datum"] != null
? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0),
mode: json["Modja"] != null ? Category.fromJson(json["Modja"]) : null,
// subjectIndex: json["OrarendiOraOraszama"],
// subjectName: json["TantargyNeve"] ?? "",
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
description: (json["Temaja"] ?? "").trim(),
group: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Uid"] ?? ""
: "",
json: json,
);
}
}

View File

@@ -0,0 +1,158 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'package:refilc/utils/format.dart';
import 'category.dart';
import 'subject.dart';
import 'teacher.dart';
class Grade {
Map? json;
String id;
DateTime date;
GradeValue value;
Teacher teacher;
String description;
GradeType type;
String groupId;
GradeSubject subject;
Category? gradeType;
Category mode;
DateTime writeDate;
DateTime seenDate;
String form;
Grade({
required this.id,
required this.date,
required this.value,
required this.teacher,
required this.description,
required this.type,
required this.groupId,
required this.subject,
this.gradeType,
required this.mode,
required this.writeDate,
required this.seenDate,
required this.form,
this.json,
});
factory Grade.fromJson(Map json) {
return Grade(
id: json["Uid"] ?? "",
date: json["KeszitesDatuma"] != null
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
: DateTime(0),
value: GradeValue(
json["SzamErtek"] ?? 0,
json["SzovegesErtek"] ?? "",
json["SzovegesErtekelesRovidNev"] ?? "",
json["SulySzazalekErteke"] ?? 0,
percentage: json["ErtekFajta"] != null
? json["ErtekFajta"]["Uid"] == "3,Szazalekos"
: false,
),
teacher: Teacher.fromString((json["ErtekeloTanarNeve"] ?? "").trim()),
description: json["Tema"] ?? "",
type: json["Tipus"] != null
? Category.getGradeType(json["Tipus"]["Nev"])
: GradeType.unknown,
groupId: (json["OsztalyCsoport"] ?? {})["Uid"] ?? "",
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
gradeType: json["ErtekFajta"] != null
? Category.fromJson(json["ErtekFajta"])
: null,
mode: Category.fromJson(json["Mod"] ?? {}),
writeDate: json["RogzitesDatuma"] != null
? DateTime.parse(json["RogzitesDatuma"]).toLocal()
: DateTime(0),
seenDate: json["LattamozasDatuma"] != null
? DateTime.parse(json["LattamozasDatuma"]).toLocal()
: DateTime(0),
form: (json["Jelleg"] ?? "Na") != "Na" ? json["Jelleg"] : "",
json: json,
);
}
bool compareTo(dynamic other) {
if (runtimeType != other.runtimeType) return false;
if (id == other.id && seenDate == other.seenDate) {
return true;
}
return false;
}
}
class GradeValue {
int _value;
set value(int v) => _value = v;
int get value {
String _valueName = valueName.toLowerCase().specialChars();
if (_value == 0 &&
["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
switch (_valueName) {
case "peldas":
return 5;
case "jo":
return 4;
case "valtozo":
return 3;
case "rossz":
return 2;
case "hanyag":
return 1;
// other
case "jeles":
return 5;
case "kozepes":
return 3;
case "elegseges":
return 2;
case "elegtelen":
return 1;
}
}
return _value;
}
String _valueName;
set valueName(String v) => _valueName = v;
String get valueName => _valueName.split("(")[0];
String shortName;
int _weight;
set weight(int v) => _weight = v;
int get weight {
String _valueName = valueName.toLowerCase().specialChars();
if (_value == 0 &&
["peldas", "jo", "valtozo", "rossz", "hanyag"].contains(_valueName)) {
return 0;
}
return _weight;
}
final bool _percentage;
bool get percentage => _percentage;
GradeValue(int value, String valueName, this.shortName, int weight,
{bool percentage = false})
: _value = value,
_valueName = valueName,
_weight = weight,
_percentage = percentage;
}
enum GradeType {
midYear,
firstQ,
secondQ,
halfYear,
thirdQ,
fourthQ,
endYear,
levelExam,
ghost,
unknown
}

View File

@@ -0,0 +1,23 @@
import 'package:refilc_kreta_api/models/subject.dart';
class GroupAverage {
String uid;
double average;
GradeSubject subject;
Map json;
GroupAverage(
{required this.uid,
required this.average,
required this.subject,
this.json = const {}});
factory GroupAverage.fromJson(Map json) {
return GroupAverage(
uid: json["Uid"] ?? "",
average: json["OsztalyCsoportAtlag"] ?? 0,
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
json: json,
);
}
}

View File

@@ -0,0 +1,86 @@
import 'package:refilc_kreta_api/client/api.dart';
import 'subject.dart';
import 'teacher.dart';
class Homework {
Map? json;
DateTime date;
DateTime lessonDate;
DateTime deadline;
bool byTeacher;
bool homeworkEnabled;
Teacher teacher;
String content;
GradeSubject subject;
String group;
List<HomeworkAttachment> attachments;
String id;
Homework({
required this.date,
required this.lessonDate,
required this.deadline,
required this.byTeacher,
required this.homeworkEnabled,
required this.teacher,
required this.content,
required this.subject,
required this.group,
required this.attachments,
required this.id,
this.json,
});
factory Homework.fromJson(Map json) {
return Homework(
id: json["Uid"] ?? "",
date: json["RogzitesIdopontja"] != null
? DateTime.parse(json["RogzitesIdopontja"]).toLocal()
: DateTime(0),
lessonDate: json["FeladasDatuma"] != null
? DateTime.parse(json["FeladasDatuma"]).toLocal()
: DateTime(0),
deadline: json["HataridoDatuma"] != null
? DateTime.parse(json["HataridoDatuma"]).toLocal()
: DateTime(0),
byTeacher: json["IsTanarRogzitette"] ?? true,
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
teacher: Teacher.fromString((json["RogzitoTanarNeve"] ?? "").trim()),
content: (json["Szoveg"] ?? "").trim(),
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
group: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Uid"] ?? ""
: "",
attachments: ((json["Csatolmanyok"] ?? []) as List)
.cast<Map>()
.map((Map json) => HomeworkAttachment.fromJson(json))
.toList(),
json: json,
);
}
}
class HomeworkAttachment {
Map? json;
String id;
String name;
String type;
HomeworkAttachment(
{required this.id, this.name = "", this.type = "", this.json});
factory HomeworkAttachment.fromJson(Map json) {
return HomeworkAttachment(
id: json["Uid"] ?? "",
name: json["Nev"] ?? "",
type: json["Tipus"] ?? "",
json: json,
);
}
String downloadUrl(String iss) =>
KretaAPI.downloadHomeworkAttachments(iss, id, type);
bool get isImage =>
name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png");
}

View File

@@ -0,0 +1,119 @@
import 'subject.dart';
import 'category.dart';
import 'teacher.dart';
class Lesson {
Map? json;
Category? status;
DateTime date;
GradeSubject subject;
String lessonIndex;
int? lessonYearIndex;
Teacher? substituteTeacher;
Teacher teacher;
bool homeworkEnabled;
DateTime start;
DateTime end;
bool studentPresence;
String homeworkId;
String exam;
String id;
Category? type;
String description;
String room;
String groupName;
String name;
bool online;
bool isEmpty;
bool isSeen;
Lesson({
this.status,
required this.date,
required this.subject,
required this.lessonIndex,
this.lessonYearIndex,
this.substituteTeacher,
required this.teacher,
this.homeworkEnabled = false,
required this.start,
required this.end,
this.studentPresence = true,
required this.homeworkId,
this.exam = "",
required this.id,
this.type,
required this.description,
required this.room,
required this.groupName,
required this.name,
this.online = false,
this.isEmpty = false,
this.json,
this.isSeen = false,
});
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Lesson && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
factory Lesson.fromJson(Map json) {
return Lesson(
id: json["Uid"] ?? "",
status:
json["Allapot"] != null ? Category.fromJson(json["Allapot"]) : null,
date: json["Datum"] != null
? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0),
subject: GradeSubject.fromJson(json["Tantargy"] ?? {}),
lessonIndex: json["Oraszam"] != null ? json["Oraszam"].toString() : "+",
lessonYearIndex: json["OraEvesSorszama"],
substituteTeacher: json["HelyettesTanarNeve"] != null
? Teacher.fromString((json["HelyettesTanarNeve"]).trim())
: null,
teacher: Teacher.fromString((json["TanarNeve"] ?? "").trim()),
homeworkEnabled: json["IsTanuloHaziFeladatEnabled"] ?? false,
start: json["KezdetIdopont"] != null
? DateTime.parse(json["KezdetIdopont"]).toLocal()
: DateTime(0),
studentPresence: json["TanuloJelenlet"] != null
? (json["TanuloJelenlet"]["Nev"] ?? "") == "Hianyzas"
? false
: true
: true,
end: json["VegIdopont"] != null
? DateTime.parse(json["VegIdopont"]).toLocal()
: DateTime(0),
homeworkId: json["HaziFeladatUid"] ?? "",
exam: json["BejelentettSzamonkeresUid"] ?? "",
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
description: json["Tema"] ?? "",
room: ((json["TeremNeve"] ?? "").split("_").join(" ") as String)
.replaceAll(RegExp(r" ?terem ?", caseSensitive: false), ""),
groupName: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Nev"] ?? ""
: "",
name: json["Nev"] ?? "",
online: json["IsDigitalisOra"] ?? false,
isEmpty: json['isEmpty'] ?? false,
json: json,
isSeen: false);
}
int? getFloor() {
final match = RegExp(r"(\d{3})").firstMatch(room);
if (match != null) {
final floorNumber = int.tryParse(match[0] ?? "");
if (floorNumber != null) {
return (floorNumber / 100).floor();
}
}
return null;
}
bool get isChanged => status?.name == "Elmaradt" || substituteTeacher != null;
bool get swapDesc => room.length > 8;
}

View File

@@ -0,0 +1,211 @@
import 'recipient.dart';
import 'attachment.dart';
class Message {
Map? json;
int id;
int? replyId;
int messageId;
int? conversationId;
bool seen;
bool deleted;
DateTime date;
String author;
String content;
String subject;
MessageType? type;
List<Recipient> recipients;
List<Attachment> attachments;
bool isSeen;
Message({
required this.id,
required this.messageId,
required this.seen,
required this.deleted,
required this.date,
required this.author,
required this.content,
required this.subject,
this.type,
required this.recipients,
required this.attachments,
this.replyId,
this.conversationId,
this.json,
this.isSeen = false,
});
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Message && runtimeType == other.runtimeType && id == other.id;
@override
int get hashCode => id.hashCode;
factory Message.fromJson(Map json, {MessageType? forceType}) {
Map message = json["uzenet"];
MessageType? type = forceType;
if (type == null) {
switch (json["tipus"]["kod"]) {
case "BEERKEZETT":
type = MessageType.inbox;
break;
case "ELKULDOTT":
type = MessageType.sent;
break;
case "PISZKOZAT":
type = MessageType.draft;
break;
}
if (json["isToroltElem"] == true) type = MessageType.trash;
}
return Message(
id: json["azonosito"],
messageId: message["azonosito"],
seen: json["isElolvasva"] ?? false,
deleted: json["isToroltElem"] ?? false,
date: message["kuldesDatum"] != null
? DateTime.parse(message["kuldesDatum"]).toLocal()
: DateTime(0),
author: (message["feladoNev"] ?? "").trim(),
content: message["szoveg"].replaceAll("\r", "") ?? "",
subject: message["targy"] ?? "",
type: type,
recipients: (message["cimzettLista"] as List)
.cast<Map>()
.map((Map recipient) => Recipient.fromJson(recipient))
.toList(),
attachments: (message["csatolmanyok"] as List)
.cast<Map>()
.map((Map attachment) => Attachment.fromJson(attachment))
.toList(),
replyId: message["elozoUzenetAzonosito"],
conversationId: message["beszelgetesAzonosito"],
json: json,
isSeen: false,
);
}
bool compareTo(dynamic other) {
if (runtimeType != other.runtimeType) return false;
return id == other.id && seen == other.seen && deleted == other.deleted;
}
}
enum MessageType { inbox, sent, trash, draft }
class Conversation {
int id;
late List<Message> _messages;
List<Message> get messages => _messages;
Conversation({required this.id, List<Message> messages = const []}) {
_messages = List.from(messages);
}
void sort() => _messages.sort((a, b) => -a.date.compareTo(b.date));
void add(Message message) => _messages.add(message);
Message get newest => _messages.first;
}
// sendable message object and it's things
class SendMessage {
Map? json;
int id;
int lastMessageId;
String? subject;
String text;
DateTime sentDate;
String senderRank;
String senderName;
List<Attachment>? attachments;
List<SendRecipient> recipients;
SendMessage({
required this.id,
required this.lastMessageId,
this.subject,
required this.text,
required this.sentDate,
required this.senderRank,
required this.senderName,
this.attachments,
required this.recipients,
});
}
class SendRecipient {
Map? json;
int? id;
int? kretaId;
String? name;
SendRecipientType type;
SendRecipient({
required this.id,
required this.kretaId,
required this.name,
required this.type,
this.json,
});
factory SendRecipient.fromJson(Map json, SendRecipientType type) {
return SendRecipient(
id: json['kretaAzonosito'] ?? 0,
kretaId: json['kretaAzonosito'],
name: json['nev'],
type: type,
json: json,
);
}
Object get kretaJson => {
'azonosito': kretaId ?? 0,
'kretaAzonosito': kretaId ?? 0,
'nev': name ?? 'Teszt Lajos',
'tipus': {
'azonosito': type.id,
'kod': type.code,
'leiras': type.description,
'nev': type.name,
'rovidNev': type.shortName,
}
};
}
class SendRecipientType {
Map? json;
int id;
String code;
String description;
String name;
String shortName;
SendRecipientType({
required this.id,
required this.code,
required this.description,
required this.name,
required this.shortName,
this.json,
});
factory SendRecipientType.fromJson(Map json) {
return SendRecipientType(
id: json['azonosito'],
code: json['kod'],
description: json['leiras'],
name: json['nev'],
shortName: json['rovidNev'],
json: json,
);
}
}
enum AddresseeType { teachers, directorate }

View File

@@ -0,0 +1,51 @@
import 'category.dart';
import 'teacher.dart';
class Note {
Map? json;
String id;
String title;
DateTime date;
DateTime submitDate;
Teacher teacher;
DateTime seenDate;
String groupId;
String content;
Category? type;
Note({
required this.id,
required this.title,
required this.date,
required this.submitDate,
required this.teacher,
required this.seenDate,
required this.groupId,
required this.content,
this.type,
this.json,
});
factory Note.fromJson(Map json) {
return Note(
id: json["Uid"] ?? "",
title: json["Cim"] ?? "",
date: json["Datum"] != null
? DateTime.parse(json["Datum"]).toLocal()
: DateTime(0),
submitDate: json["KeszitesDatuma"] != null
? DateTime.parse(json["KeszitesDatuma"]).toLocal()
: DateTime(0),
teacher: Teacher.fromString((json["KeszitoTanarNeve"] ?? "").trim()),
seenDate: json["LattamozasDatuma"] != null
? DateTime.parse(json["LattamozasDatuma"]).toLocal()
: DateTime(0),
groupId: json["OsztalyCsoport"] != null
? json["OsztalyCsoport"]["Uid"] ?? ""
: "",
content: json["Tartalom"].replaceAll("\r", "") ?? "",
type: json["Tipus"] != null ? Category.fromJson(json["Tipus"]) : null,
json: json,
);
}
}

View File

@@ -0,0 +1,56 @@
class Recipient {
Map? json;
int id;
String? studentId; // oktatasi azonosito
int kretaId;
String name;
RecipientCategory? category;
Recipient({
required this.id,
this.studentId,
required this.name,
required this.kretaId,
this.category,
this.json,
});
factory Recipient.fromJson(Map json) {
return Recipient(
id: json["azonosito"],
name: json["nev"] ?? "",
kretaId: json["kretaAzonosito"],
category: json["tipus"] != null ? RecipientCategory.fromJson(json["tipus"]) : null,
json: json,
);
}
}
class RecipientCategory {
Map? json;
int id;
String code;
String shortName;
String name;
String description;
RecipientCategory({
required this.id,
required this.code,
required this.shortName,
required this.name,
required this.description,
this.json,
});
factory RecipientCategory.fromJson(Map json) {
return RecipientCategory(
id: json["azonosito"],
code: json["kod"] ?? "",
shortName: json["rovidNev"] ?? "",
name: json["nev"] ?? "",
description: json["leiras"] ?? "",
json: json,
);
}
}

View File

@@ -0,0 +1,19 @@
class School {
String instituteCode;
String name;
String city;
School({
required this.instituteCode,
required this.name,
required this.city,
});
factory School.fromJson(Map json) {
return School(
instituteCode: json["instituteCode"] ?? "",
name: (json["name"] ?? "").trim(),
city: json["city"] ?? "",
);
}
}

View File

@@ -0,0 +1,61 @@
import 'school.dart';
import 'package:refilc/utils/format.dart';
class Student {
Map? json;
String id;
String name;
School school;
DateTime birth;
String yearId;
String? address;
String? groupId;
List<String> parents;
String? className;
Student({
required this.id,
required this.name,
required this.school,
required this.birth,
required this.yearId,
this.address,
required this.parents,
this.json,
});
factory Student.fromJson(Map json) {
List<String> parents = [];
parents = ((json["Gondviselok"] ?? []) as List)
.cast<Map>()
.map((e) => e["Nev"] ?? "")
.toList()
.cast<String>();
if (json["AnyjaNeve"] != null) parents.insert(0, json["AnyjaNeve"]);
parents = parents.map((e) => e.capitalize()).toList(); // fix name casing
parents = parents.toSet().toList(); // remove duplicates
return Student(
id: json["Uid"] ?? "",
name: (json["Nev"] ?? json["SzuletesiNev"] ?? "").trim(),
school: School(
instituteCode: json["IntezmenyAzonosito"] ?? "",
name: json["IntezmenyNev"] ?? "",
city: "",
),
birth: json["SzuletesiDatum"] != null
? DateTime.parse(json["SzuletesiDatum"]).toLocal()
: DateTime(0),
yearId: json["TanevUid"] ?? "",
address: json["Cimek"] != null
? json["Cimek"].length > 0
? json["Cimek"][0]
: null
: null,
parents: parents,
json: json,
);
}
}

View File

@@ -0,0 +1,39 @@
import 'category.dart';
class GradeSubject {
String id;
Category category;
String name;
String? renamedTo;
double? customRounding;
bool get isRenamed => renamedTo != null;
bool get hasCustomRounding => customRounding != null;
GradeSubject({
required this.id,
required this.category,
required this.name,
this.renamedTo,
// v5
this.customRounding,
});
factory GradeSubject.fromJson(Map json) {
final id = json["Uid"] ?? "";
return GradeSubject(
id: id,
category: Category.fromJson(json["Kategoria"] ?? {}),
name: (json["Nev"] ?? "").trim(),
);
}
@override
bool operator ==(other) {
if (other is! GradeSubject) return false;
return id == other.id;
}
@override
int get hashCode => id.hashCode;
}

View File

@@ -0,0 +1,34 @@
import 'package:refilc/utils/format.dart';
class Teacher {
String id;
String name;
String? renamedTo;
bool get isRenamed => renamedTo != null;
Teacher({required this.id, required this.name, this.renamedTo});
factory Teacher.fromJson(Map json) {
return Teacher(
id: json["Uid"] ?? "",
name: (json["Nev"] ?? "").trim(),
);
}
factory Teacher.fromString(String string) {
return Teacher(
id: string.trim().replaceAll(' ', '').toLowerCase().specialChars(),
name: string.trim(),
);
}
@override
bool operator ==(other) {
if (other is! Teacher) return false;
return id == other.id;
}
@override
int get hashCode => id.hashCode;
}

View File

@@ -0,0 +1,47 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers
import 'package:refilc_kreta_api/controllers/timetable_controller.dart';
class Week {
DateTime start;
DateTime end;
Week({required this.start, required this.end});
factory Week.current() => Week.fromDate(DateTime.now());
factory Week.fromId(int id) {
DateTime _now = TimetableController.getSchoolYearStart()
.add(Duration(days: id * DateTime.daysPerWeek));
DateTime now = DateTime(_now.year, _now.month, _now.day);
return Week(
start: now.subtract(Duration(days: now.weekday - 1)),
end: now.add(Duration(days: DateTime.daysPerWeek - now.weekday)),
);
}
factory Week.fromDate(DateTime date) {
// fix #32
DateTime _date = DateTime(date.year, date.month, date.day);
return Week(
start: _date.subtract(Duration(days: _date.weekday - 1)),
end: _date.add(Duration(days: DateTime.daysPerWeek - _date.weekday)),
);
}
Week next() => Week.fromDate(start.add(const Duration(days: 8)));
int get id =>
-(TimetableController.getSchoolYearStart().difference(start).inDays /
DateTime.daysPerWeek)
.floor();
@override
String toString() => "Week(start: $start, end: $end)";
@override
bool operator ==(Object other) => other is Week && id == other.id;
@override
int get hashCode => id;
}

View File

@@ -0,0 +1,107 @@
// ignore_for_file: no_leading_underscores_for_local_identifiers, use_build_context_synchronously
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/absence.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class AbsenceProvider with ChangeNotifier {
late List<Absence> _absences;
late BuildContext _context;
List<Absence> get absences => _absences;
AbsenceProvider({
List<Absence> initialAbsences = const [],
required BuildContext context,
}) {
_absences = List.castFrom(initialAbsences);
_context = context;
if (_absences.isEmpty) restore();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load absences from the database
if (userId != null) {
var dbAbsences =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getAbsences(userId: userId);
_absences = dbAbsences;
await convertBySettings();
}
}
// for renamed subjects
Future<void> convertBySettings() async {
final _database = Provider.of<DatabaseProvider>(_context, listen: false);
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(
userId:
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
Map<String, String> renamedTeachers =
(await _database.query.getSettings(_database)).renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(
userId:
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
// v5
Map<String, String> customRoundings = await _database.userQuery
.getRoundings(
userId:
Provider.of<UserProvider>(_context, listen: false).user!.id);
for (Absence absence in _absences) {
absence.subject.renamedTo = renamedSubjects.isNotEmpty
? renamedSubjects[absence.subject.id]
: null;
absence.teacher.renamedTo = renamedTeachers.isNotEmpty
? renamedTeachers[absence.teacher.id]
: null;
// v5
absence.subject.customRounding = customRoundings.isNotEmpty
? double.parse(customRoundings[absence.subject.id] ?? '5.0')
: null;
}
notifyListeners();
}
// Fetches Absences from the Kreta API then stores them in the database
Future<void> fetch() async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Absences for User null";
String iss = user.instituteCode;
List? absencesJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.absences(iss));
if (absencesJson == null) throw "Cannot fetch Absences for User ${user.id}";
List<Absence> absences =
absencesJson.map((e) => Absence.fromJson(e)).toList();
if (absences.isNotEmpty || _absences.isNotEmpty) await store(absences);
}
// Stores Absences in the database
Future<void> store(List<Absence> absences) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Absences for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeAbsences(absences, userId: userId);
_absences = absences;
await convertBySettings();
}
}

View File

@@ -0,0 +1,65 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/event.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class EventProvider with ChangeNotifier {
late List<Event> _events;
late BuildContext _context;
List<Event> get events => _events;
EventProvider({
List<Event> initialEvents = const [],
required BuildContext context,
}) {
_events = List.castFrom(initialEvents);
_context = context;
if (_events.isEmpty) restore();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load events from the database
if (userId != null) {
var dbEvents =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getEvents(userId: userId);
_events = dbEvents;
notifyListeners();
}
}
// Fetches Events from the Kreta API then stores them in the database
Future<void> fetch() async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Events for User null";
String iss = user.instituteCode;
List? eventsJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.events(iss));
if (eventsJson == null) throw "Cannot fetch Events for User ${user.id}";
List<Event> events = eventsJson.map((e) => Event.fromJson(e)).toList();
if (events.isNotEmpty || _events.isNotEmpty) await store(events);
}
// Stores Events in the database
Future<void> store(List<Event> events) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Events for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeEvents(events, userId: userId);
_events = events;
notifyListeners();
}
}

View File

@@ -0,0 +1,94 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/exam.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ExamProvider with ChangeNotifier {
late List<Exam> _exams;
late BuildContext _context;
List<Exam> get exams => _exams;
ExamProvider({
List<Exam> initialExams = const [],
required BuildContext context,
}) {
_exams = List.castFrom(initialExams);
_context = context;
if (_exams.isEmpty) restore();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load exams from the database
if (userId != null) {
var dbExams = await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getExams(userId: userId);
_exams = dbExams;
notifyListeners();
await convertBySettings();
}
}
// for renamed subjects
Future<void> convertBySettings() async {
final database = Provider.of<DatabaseProvider>(_context, listen: false);
Map<String, String> renamedSubjects =
(await database.query.getSettings(database)).renamedSubjectsEnabled
? await database.userQuery.renamedSubjects(
userId:
// ignore: use_build_context_synchronously
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
Map<String, String> renamedTeachers =
(await database.query.getSettings(database)).renamedTeachersEnabled
? await database.userQuery.renamedTeachers(
userId:
// ignore: use_build_context_synchronously
Provider.of<UserProvider>(_context, listen: false).user!.id)
: {};
for (Exam exam in _exams) {
exam.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[exam.subject.id] : null;
exam.teacher.renamedTo =
renamedTeachers.isNotEmpty ? renamedTeachers[exam.teacher.id] : null;
}
notifyListeners();
}
// Fetches Exams from the Kreta API then stores them in the database
Future<void> fetch() async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Exams for User null";
String iss = user.instituteCode;
List? examsJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.exams(iss));
if (examsJson == null) throw "Cannot fetch Exams for User ${user.id}";
List<Exam> exams = examsJson.map((e) => Exam.fromJson(e)).toList();
if (exams.isNotEmpty || _exams.isNotEmpty) await store(exams);
}
// Stores Exams in the database
Future<void> store(List<Exam> exams) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Exams for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeExams(exams, userId: userId);
_exams = exams;
notifyListeners();
await convertBySettings();
}
}

View File

@@ -0,0 +1,186 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/grade.dart';
import 'package:refilc_kreta_api/models/group_average.dart';
import 'package:refilc_kreta_api/providers/grade_provider.i18n.dart';
import 'package:flutter/material.dart';
class GradeProvider with ChangeNotifier {
// Private
late List<Grade> _grades;
late DateTime _lastSeen;
late String _groups;
List<GroupAverage> _groupAvg = [];
late final SettingsProvider _settings;
late final UserProvider _user;
late final DatabaseProvider _database;
late final KretaClient _kreta;
// Public
List<Grade> get grades => _grades;
DateTime get lastSeenDate =>
_settings.gradeOpeningFun ? _lastSeen : DateTime(3000);
String get groups => _groups;
List<GroupAverage> get groupAverages => _groupAvg;
GradeProvider({
List<Grade> initialGrades = const [],
required SettingsProvider settings,
required UserProvider user,
required DatabaseProvider database,
required KretaClient kreta,
}) {
_settings = settings;
_user = user;
_database = database;
_kreta = kreta;
_grades = List.castFrom(initialGrades);
_lastSeen = DateTime.now();
if (_grades.isEmpty) restore();
}
Future<void> seenAll() async {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
_lastSeen = DateTime.now();
}
}
Future<void> unseenAll() async {
String? userId = _user.id;
if (userId != null) {
final userStore = _database.userStore;
userStore.storeLastSeenGrade(DateTime(1969), userId: userId);
_lastSeen = DateTime(1969);
}
}
Future<void> restore() async {
String? userId = _user.id;
// Load grades from the database
if (userId != null) {
final userQuery = _database.userQuery;
_grades = await userQuery.getGrades(userId: userId);
await convertBySettings();
_groupAvg = await userQuery.getGroupAverages(userId: userId);
notifyListeners();
DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId);
if (lastSeenDB.millisecondsSinceEpoch == 0 ||
lastSeenDB.year == 0 ||
!_settings.gradeOpeningFun) {
_lastSeen = DateTime.now();
await seenAll();
} else {
_lastSeen = lastSeenDB;
}
notifyListeners();
}
}
// good student mode, renamed subjects
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
: {};
Map<String, String> renamedTeachers = _settings.renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(userId: _user.user!.id)
: {};
// v5
Map<String, String> customRoundings =
await _database.userQuery.getRoundings(userId: _user.user!.id);
for (Grade grade in _grades) {
grade.subject.renamedTo =
renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
grade.teacher.renamedTo =
renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null;
grade.value.value =
_settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
grade.value.valueName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtek"]}'
.replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
.i18n;
grade.value.shortName = _settings.goodStudent
? "Jeles".i18n
: '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
'${grade.json!["SzovegesErtekelesRovidNev"]}'
.replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
""
? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
: grade.value.valueName;
// v5
grade.subject.customRounding = customRoundings.isNotEmpty
? double.parse(customRoundings[grade.subject.id] ?? '5.0')
: null;
}
notifyListeners();
}
// Fetches Grades from the Kreta API then stores them in the database
Future<void> fetch() async {
// test cucc
// unseenAll();
User? user = _user.user;
if (user == null) throw "Cannot fetch Grades for User null";
String iss = user.instituteCode;
List? gradesJson = await _kreta.getAPI(KretaAPI.grades(iss));
if (gradesJson == null) throw "Cannot fetch Grades for User ${user.id}";
List<Grade> grades = gradesJson.map((e) => Grade.fromJson(e)).toList();
if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
if (groupsJson == null || groupsJson.isEmpty) {
throw "Cannot fetch Groups for User ${user.id}";
}
_groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
List? groupAvgJson =
await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
if (groupAvgJson == null) {
throw "Cannot fetch Class Averages for User ${user.id}";
}
final groupAvgs =
groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
await storeGroupAvg(groupAvgs);
}
// Stores Grades in the database
Future<void> store(List<Grade> grades) async {
User? user = _user.user;
if (user == null) throw "Cannot store Grades for User null";
String userId = user.id;
await _database.userStore.storeGrades(grades, userId: userId);
_grades = grades;
await convertBySettings();
}
Future<void> storeGroupAvg(List<GroupAverage> groupAvgs) async {
_groupAvg = groupAvgs;
User? user = _user.user;
if (user == null) throw "Cannot store Grades for User null";
String userId = user.id;
await _database.userStore.storeGroupAverages(groupAvgs, userId: userId);
notifyListeners();
}
}

View File

@@ -0,0 +1,33 @@
import 'package:i18n_extension/i18n_extension.dart';
extension Localization on String {
static final _t = Translations.byLocale("hu_hu") +
{
"en_en": {
"Elégtelen": "Fail",
"Elégséges": "Warning but passing",
"Közepes": "Passed",
"": "Good",
"Jeles": "Excellent"
},
"hu_hu": {
"Elégtelen": "Elégtelen",
"Elégséges": "Elégséges",
"Közepes": "Közepes",
"": "",
"Jeles": "Jeles"
},
"de_de": {
"Elégtelen": "Ungenügend",
"Elégséges": "Mangelhaft",
"Közepes": "Ausreichend",
"": "Befriedigend",
"Jeles": "Gut"
},
};
String get i18n => localize(this, _t);
String fill(List<Object> params) => localizeFill(this, params);
String plural(int value) => localizePlural(value, this, _t);
String version(Object modifier) => localizeVersion(modifier, this, _t);
}

View File

@@ -0,0 +1,129 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
// import 'package:refilc/models/settings.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/homework.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class HomeworkProvider with ChangeNotifier {
// Private
// late final SettingsProvider _settings;
late final UserProvider _user;
late final DatabaseProvider _database;
// Public
late List<Homework> _homework;
late BuildContext _context;
List<Homework> get homework => _homework;
HomeworkProvider({
List<Homework> initialHomework = const [],
required BuildContext context,
required DatabaseProvider database,
required UserProvider user,
}) {
_homework = List.castFrom(initialHomework);
_context = context;
_database = database;
_user = user;
if (_homework.isEmpty) restore();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load homework from the database
if (userId != null) {
var dbHomework =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getHomework(userId: userId);
_homework = dbHomework;
await convertBySettings();
}
}
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.id!)
: {};
Map<String, String> renamedTeachers =
(await _database.query.getSettings(_database)).renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(userId: _user.id!)
: {};
for (Homework homework in _homework) {
homework.subject.renamedTo = renamedSubjects.isNotEmpty
? renamedSubjects[homework.subject.id]
: null;
homework.teacher.renamedTo = renamedTeachers.isNotEmpty
? renamedTeachers[homework.teacher.id]
: null;
}
notifyListeners();
}
// Fetches Homework from the Kreta API then stores them in the database
Future<void> fetch({DateTime? from, bool db = true}) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Homework for User null";
String iss = user.instituteCode;
List? homeworkJson = [];
try {
Iterable hwjson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, start: from));
homeworkJson = List.from(hwjson.map((model) => model));
} catch (e) {
if (kDebugMode) print(e);
// error fetcing homework (unknown error)
}
if (homeworkJson == null) {
if (kDebugMode) print("Cannot fetch Homework for User ${user.id}");
return;
}
List<Homework> homework = [];
await Future.forEach(homeworkJson.cast<Map>(), (Map hw) async {
Map? e = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.homework(iss, id: hw["Uid"]));
// Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
// ? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
// : {};
if (e != null) {
Homework hmwrk = Homework.fromJson(e);
// hw.subject.renamedTo =
// renamedSubjects.isNotEmpty ? renamedSubjects[hw.subject.id] : null;
homework.add(hmwrk);
}
});
if (homework.isEmpty && _homework.isEmpty) return;
if (db) await store(homework);
_homework = homework;
notifyListeners();
await convertBySettings();
}
// Stores Homework in the database
Future<void> store(List<Homework> homework) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Homework for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeHomework(homework, userId: userId);
}
}

View File

@@ -0,0 +1,268 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:convert';
import 'dart:math';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/message.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class MessageProvider with ChangeNotifier {
late List<Message> _messages;
late List<SendRecipient> _recipients;
late BuildContext _context;
List<Message> get messages => _messages;
List<SendRecipient> get recipients => _recipients;
MessageProvider({
List<Message> initialMessages = const [],
required BuildContext context,
}) {
_messages = List.castFrom(initialMessages);
_recipients = [];
_context = context;
if (_messages.isEmpty) restore();
if (_recipients.isEmpty) restoreRecipients();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load messages from the database
if (userId != null) {
var dbMessages =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getMessages(userId: userId);
_messages = dbMessages;
notifyListeners();
}
}
// Fetches all types of Messages
Future<void> fetchAll() =>
Future.forEach(MessageType.values, (MessageType v) => fetch(type: v));
// Fetches Messages from the Kreta API then stores them in the database
Future<void> fetch({MessageType type = MessageType.inbox}) async {
// Check Message Type
if (type == MessageType.draft) return;
String messageType =
["beerkezett", "elkuldott", "torolt"].elementAt(type.index);
// Check User
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Messages for User null";
// Get messages
List? messagesJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.messages(messageType));
if (messagesJson == null) throw "Cannot fetch Messages for User ${user.id}";
// Parse messages
List<Message> messages = [];
await Future.wait(List.generate(messagesJson.length, (index) {
return () async {
Map message = messagesJson.cast<Map>()[index];
Map? messageJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.message(message["azonosito"].toString()));
if (messageJson != null) {
messages.add(Message.fromJson(messageJson, forceType: type));
}
}();
}));
await store(messages, type);
}
// Stores Messages in the database
Future<void> store(List<Message> messages, MessageType type) async {
// Only store the specified type
_messages.removeWhere((m) => m.type == type);
_messages.addAll(messages);
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Messages for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeMessages(_messages, userId: userId);
notifyListeners();
}
// restore recipients
Future<void> restoreRecipients() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load messages from the database
if (userId != null) {
var dbRecipients =
await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getRecipients(userId: userId);
_recipients = dbRecipients;
notifyListeners();
}
}
// fetch all recipients
Future<void> fetchAllRecipients() => Future.forEach(
AddresseeType.values, (AddresseeType v) => fetchRecipients(type: v));
// fetch recipients
Future<void> fetchRecipients(
{AddresseeType type = AddresseeType.teachers}) async {
Map<AddresseeType, SendRecipientType> addressable = {};
// check user
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Messages for User null";
// get categories
List? availableCategoriesJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.recipientCategories);
// print(availableCategoriesJson);
// get recipients
List? recipientTeachersJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.recipientTeachers);
List? recipientDirectorateJson =
await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.recipientDirectorate);
if (availableCategoriesJson == null ||
recipientTeachersJson == null ||
recipientDirectorateJson == null) {
throw "Cannot fetch Recipients for User ${user.id}";
}
for (var e in availableCategoriesJson) {
// print(e);
switch (e['kod']) {
case 'TANAR':
addressable
.addAll({AddresseeType.teachers: SendRecipientType.fromJson(e)});
break;
case 'IGAZGATOSAG':
addressable.addAll(
{AddresseeType.directorate: SendRecipientType.fromJson(e)});
break;
default:
break;
}
}
// parse recipients
List<SendRecipient> recipients = [];
if (addressable.containsKey(AddresseeType.teachers) &&
type == AddresseeType.teachers) {
recipients.addAll(recipientTeachersJson.map((e) =>
SendRecipient.fromJson(e, addressable[AddresseeType.teachers]!)));
}
if (addressable.containsKey(AddresseeType.directorate) &&
type == AddresseeType.directorate) {
recipients.addAll(recipientDirectorateJson.map((e) =>
SendRecipient.fromJson(e, addressable[AddresseeType.directorate]!)));
}
// if (kDebugMode) {
// print(addressable);
// print(recipients);
// print(recipients.first.json);
// }
await storeRecipients(recipients, type);
}
// store recipients
Future<void> storeRecipients(
List<SendRecipient> recipients, AddresseeType type) async {
_recipients.removeWhere((r) => (type == AddresseeType.teachers
? (r.type.code == 'TANAR')
: (type == AddresseeType.directorate
? (r.type.code == 'IGAZGATOSAG')
: r.type.code != '')));
_recipients.addAll(recipients);
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Recipients for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeRecipients(_recipients, userId: userId);
notifyListeners();
}
// send message
Future<String?> sendMessage({
required List<SendRecipient> recipients,
String subject = "Nincs tárgy",
required String messageText,
}) async {
List<Object> recipientList = [];
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot send Message as User null";
// for (var r in recipients) {
// recipientList.add({
// "azonosito": r.id ?? "",
// "kretaAzonosito": r.kretaId ?? "",
// "nev": r.name ?? "Teszt Lajos",
// "tipus": {
// "kod": r.type.code,
// "leiras": r.type.description,
// "azonosito": r.type.id,
// "nev": r.type.name,
// "rovidNev": r.type.shortName,
// }
// });
// }
recipientList.addAll(recipients.map((e) => e.kretaJson));
Map body = {
"cimzettLista": recipientList,
"csatolmanyok": [],
"azonosito": (Random().nextInt(10000) + 10000),
"feladoNev": user.name,
"feladoTitulus": user.role == Role.parent ? "Szülő" : "Diák",
"kuldesDatum": DateTime.now().toIso8601String(),
"targy": subject,
"szoveg": messageText,
// "elozoUzenetAzonosito": 0,
};
Map<String, String> headers = {
"content-type": "application/json",
};
var res = await Provider.of<KretaClient>(_context, listen: false).postAPI(
KretaAPI.sendMessage,
autoHeader: true,
json: true,
body: json.encode(body),
headers: headers,
);
if (res!['hibakod'] == 'UzenetKuldesEngedelyRule') {
return 'send_permission_error';
}
return 'successfully_sent';
}
}

View File

@@ -0,0 +1,64 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/note.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class NoteProvider with ChangeNotifier {
late List<Note> _notes;
late BuildContext _context;
List<Note> get notes => _notes;
NoteProvider({
List<Note> initialNotes = const [],
required BuildContext context,
}) {
_notes = List.castFrom(initialNotes);
_context = context;
if (_notes.isEmpty) restore();
}
Future<void> restore() async {
String? userId = Provider.of<UserProvider>(_context, listen: false).id;
// Load notes from the database
if (userId != null) {
var dbNotes = await Provider.of<DatabaseProvider>(_context, listen: false)
.userQuery
.getNotes(userId: userId);
_notes = dbNotes;
notifyListeners();
}
}
// Fetches Notes from the Kreta API then stores them in the database
Future<void> fetch() async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot fetch Notes for User null";
String iss = user.instituteCode;
List? notesJson = await Provider.of<KretaClient>(_context, listen: false)
.getAPI(KretaAPI.notes(iss));
if (notesJson == null) throw "Cannot fetch Notes for User ${user.id}";
List<Note> notes = notesJson.map((e) => Note.fromJson(e)).toList();
if (notes.isNotEmpty || _notes.isNotEmpty) await store(notes);
}
// Stores Notes in the database
Future<void> store(List<Note> notes) async {
User? user = Provider.of<UserProvider>(_context, listen: false).user;
if (user == null) throw "Cannot store Notes for User null";
String userId = user.id;
await Provider.of<DatabaseProvider>(_context, listen: false)
.userStore
.storeNotes(notes, userId: userId);
_notes = notes;
notifyListeners();
}
}

View File

@@ -0,0 +1,144 @@
import 'package:refilc/api/client.dart';
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/models/settings.dart';
import 'package:refilc/models/shared_theme.dart';
// import 'package:refilc/models/shared_theme.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';
class ShareProvider extends ChangeNotifier {
final UserProvider _user;
ShareProvider({
required UserProvider user,
}) : _user = user;
// Future<void> shareTheme({required SharedTheme theme}) async {
// }
// themes
Future<SharedTheme> shareCurrentTheme(BuildContext context,
{bool isPublic = false,
bool shareNick = true,
required SharedGradeColors gradeColors,
String displayName = ''}) async {
final SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false);
Map themeJson = {
'public_id': const Uuid().v4(),
'is_public': isPublic,
'nickname': shareNick ? _user.nickname : 'Anonymous',
'display_name': displayName,
'background_color': (settings.customBackgroundColor ??
SettingsProvider.defaultSettings().customBackgroundColor)
?.value,
'panels_color': (settings.customHighlightColor ??
SettingsProvider.defaultSettings().customHighlightColor)
?.value,
'accent_color': (settings.customAccentColor ??
SettingsProvider.defaultSettings().customAccentColor)
?.value ??
const Color(0xFF3D7BF4).value,
'icon_color': (settings.customIconColor ??
SettingsProvider.defaultSettings().customIconColor)
?.value ??
const Color(0x00000000).value,
'shadow_effect': settings.shadowEffect,
'theme_mode': settings.theme == ThemeMode.dark
? 'dark'
: (settings.theme == ThemeMode.light ? 'light' : null),
'font_family': settings.fontFamily,
};
SharedTheme theme = SharedTheme.fromJson(themeJson, gradeColors);
FilcAPI.addSharedTheme(theme);
return theme;
}
Future<SharedTheme?> getThemeById(BuildContext context,
{required String id}) async {
Map? themeJson = await FilcAPI.getSharedTheme(id);
if (themeJson != null) {
Map? gradeColorsJson =
await FilcAPI.getSharedGradeColors(themeJson['grade_colors_id']);
if (gradeColorsJson != null) {
SharedTheme theme = SharedTheme.fromJson(
themeJson, SharedGradeColors.fromJson(gradeColorsJson));
return theme;
}
}
return null;
}
Future<List<SharedTheme>> getAllPublicThemes(BuildContext context,
{int count = 0}) async {
List? themesJson = await FilcAPI.getAllSharedThemes(count);
List<SharedTheme> themes = [];
if (themesJson != null) {
for (var t in themesJson) {
if (t['public_id'].toString().replaceAll(' ', '') == '') continue;
if (t['grade_colors_id'].toString().replaceAll(' ', '') == '') continue;
Map? gradeColorsJson =
await FilcAPI.getSharedGradeColors(t['grade_colors_id']);
if (gradeColorsJson != null) {
SharedTheme theme = SharedTheme.fromJson(
t, SharedGradeColors.fromJson(gradeColorsJson));
themes.add(theme);
}
}
}
return themes;
}
// grade colors
Future<SharedGradeColors> shareCurrentGradeColors(
BuildContext context, {
bool isPublic = false,
bool shareNick = true,
}) async {
final SettingsProvider settings =
Provider.of<SettingsProvider>(context, listen: false);
Map gradeColorsJson = {
'public_id': const Uuid().v4(),
'is_public': isPublic,
'nickname': shareNick ? _user.nickname : 'Anonymous',
'five_color': settings.gradeColors[4].value,
'four_color': settings.gradeColors[3].value,
'three_color': settings.gradeColors[2].value,
'two_color': settings.gradeColors[1].value,
'one_color': settings.gradeColors[0].value,
};
SharedGradeColors gradeColors = SharedGradeColors.fromJson(gradeColorsJson);
FilcAPI.addSharedGradeColors(gradeColors);
return gradeColors;
}
Future<SharedGradeColors?> getGradeColorsById(BuildContext context,
{required String id}) async {
Map? gradeColorsJson = await FilcAPI.getSharedGradeColors(id);
if (gradeColorsJson != null) {
SharedGradeColors gradeColors =
SharedGradeColors.fromJson(gradeColorsJson);
return gradeColors;
}
return null;
}
}

View File

@@ -0,0 +1,165 @@
// import 'package:refilc/api/providers/user_provider.dart';
// import 'package:refilc/api/providers/database_provider.dart';
// import 'package:refilc/models/settings.dart';
// import 'package:refilc/models/user.dart';
// import 'package:refilc_kreta_api/client/api.dart';
// import 'package:refilc_kreta_api/client/client.dart';
// import 'package:refilc_kreta_api/models/grade.dart';
// import 'package:refilc_kreta_api/models/group_average.dart';
// import 'package:refilc_kreta_api/providers/grade_provider.i18n.dart';
// import 'package:flutter/material.dart';
// class SubjectProvider with ChangeNotifier {
// // Private
// late List<Grade> _grades;
// late DateTime _lastSeen;
// late String _groups;
// List<GroupAverage> _groupAvg = [];
// late final SettingsProvider _settings;
// late final UserProvider _user;
// late final DatabaseProvider _database;
// late final KretaClient _kreta;
// // Public
// List<Grade> get grades => _grades;
// DateTime get lastSeenDate =>
// _settings.gradeOpeningFun ? _lastSeen : DateTime(3000);
// String get groups => _groups;
// List<GroupAverage> get groupAverages => _groupAvg;
// SubjectProvider({
// List<Grade> initialGrades = const [],
// required SettingsProvider settings,
// required UserProvider user,
// required DatabaseProvider database,
// required KretaClient kreta,
// }) {
// _settings = settings;
// _user = user;
// _database = database;
// _kreta = kreta;
// _grades = List.castFrom(initialGrades);
// _lastSeen = DateTime.now();
// if (_grades.isEmpty) restore();
// }
// Future<void> seenAll() async {
// String? userId = _user.id;
// if (userId != null) {
// final userStore = _database.userStore;
// userStore.storeLastSeenGrade(DateTime.now(), userId: userId);
// _lastSeen = DateTime.now();
// }
// }
// Future<void> restore() async {
// String? userId = _user.id;
// // Load grades from the database
// if (userId != null) {
// final userQuery = _database.userQuery;
// _grades = await userQuery.getGrades(userId: userId);
// await convertBySettings();
// _groupAvg = await userQuery.getGroupAverages(userId: userId);
// notifyListeners();
// DateTime lastSeenDB = await userQuery.lastSeenGrade(userId: userId);
// if (lastSeenDB.millisecondsSinceEpoch == 0 ||
// lastSeenDB.year == 0 ||
// !_settings.gradeOpeningFun) {
// _lastSeen = DateTime.now();
// await seenAll();
// } else {
// _lastSeen = lastSeenDB;
// }
// notifyListeners();
// }
// }
// // good student mode, renamed subjects
// Future<void> convertBySettings() async {
// Map<String, String> renamedSubjects = _settings.renamedSubjectsEnabled
// ? await _database.userQuery.renamedSubjects(userId: _user.user!.id)
// : {};
// Map<String, String> renamedTeachers = _settings.renamedTeachersEnabled
// ? await _database.userQuery.renamedTeachers(userId: _user.user!.id)
// : {};
// for (Grade grade in _grades) {
// grade.subject.renamedTo =
// renamedSubjects.isNotEmpty ? renamedSubjects[grade.subject.id] : null;
// grade.teacher.renamedTo =
// renamedTeachers.isNotEmpty ? renamedTeachers[grade.teacher.id] : null;
// grade.value.value =
// _settings.goodStudent ? 5 : grade.json!["SzamErtek"] ?? 0;
// grade.value.valueName = _settings.goodStudent
// ? "Jeles".i18n
// : '${grade.json!["SzovegesErtek"]}'
// .replaceAll(RegExp(r'[(]+[12345]?[)]'), '')
// .i18n;
// grade.value.shortName = _settings.goodStudent
// ? "Jeles".i18n
// : '${grade.json!["SzovegesErtekelesRovidNev"]}' != "null" &&
// '${grade.json!["SzovegesErtekelesRovidNev"]}' != "-" &&
// '${grade.json!["SzovegesErtekelesRovidNev"]}'
// .replaceAll(RegExp(r'[0123456789]+[%]?'), '') !=
// ""
// ? '${grade.json!["SzovegesErtekelesRovidNev"]}'.i18n
// : grade.value.valueName;
// }
// notifyListeners();
// }
// // fetch subjects from kreten then store them
// Future<void> fetch() async {
// User? user = _user.user;
// if (user == null) throw "Cannot fetch Subjects for User null";
// String iss = user.instituteCode;
// List? gradesJson = await _kreta.getAPI(KretaAPI.subjects(iss, ""));
// if (gradesJson == null) throw "Cannot fetch Subjects for User ${user.id}";
// List<Grade> grades = gradesJson.map((e) => Grade.fromJson(e)).toList();
// if (grades.isNotEmpty || _grades.isNotEmpty) await store(grades);
// List? groupsJson = await _kreta.getAPI(KretaAPI.groups(iss));
// if (groupsJson == null || groupsJson.isEmpty) {
// throw "Cannot fetch Groups for User ${user.id}";
// }
// _groups = (groupsJson[0]["OktatasNevelesiFeladat"] ?? {})["Uid"] ?? "";
// List? groupAvgJson =
// await _kreta.getAPI(KretaAPI.groupAverages(iss, _groups));
// if (groupAvgJson == null) {
// throw "Cannot fetch Class Averages for User ${user.id}";
// }
// final groupAvgs =
// groupAvgJson.map((e) => GroupAverage.fromJson(e)).toList();
// await storeGroupAvg(groupAvgs);
// }
// // store subjects in db
// Future<void> store(List<Grade> grades) async {
// User? user = _user.user;
// if (user == null) throw "Cannot store Grades for User null";
// String userId = user.id;
// await _database.userStore.storeGrades(grades, userId: userId);
// _grades = grades;
// await convertBySettings();
// }
// Future<void> storeGroupAvg(List<GroupAverage> groupAvgs) async {
// _groupAvg = groupAvgs;
// User? user = _user.user;
// if (user == null) throw "Cannot store Grades for User null";
// String userId = user.id;
// await _database.userStore.storeGroupAverages(groupAvgs, userId: userId);
// notifyListeners();
// }
// }

View File

@@ -0,0 +1,125 @@
import 'package:refilc/api/providers/user_provider.dart';
import 'package:refilc/api/providers/database_provider.dart';
import 'package:refilc/models/user.dart';
import 'package:refilc_kreta_api/client/api.dart';
import 'package:refilc_kreta_api/client/client.dart';
import 'package:refilc_kreta_api/models/lesson.dart';
import 'package:refilc_kreta_api/models/week.dart';
import 'package:flutter/foundation.dart';
class TimetableProvider with ChangeNotifier {
Map<Week, List<Lesson>> lessons = {};
late final UserProvider _user;
late final DatabaseProvider _database;
late final KretaClient _kreta;
TimetableProvider({
required UserProvider user,
required DatabaseProvider database,
required KretaClient kreta,
}) : _user = user,
_database = database,
_kreta = kreta {
restoreUser();
}
Future<void> restoreUser() async {
String? userId = _user.id;
// Load lessons from the database
if (userId != null) {
var dbLessons = await _database.userQuery.getLessons(userId: userId);
lessons = dbLessons;
await convertBySettings();
}
}
// for renamed subjects
Future<void> convertBySettings() async {
Map<String, String> renamedSubjects =
(await _database.query.getSettings(_database)).renamedSubjectsEnabled
? await _database.userQuery.renamedSubjects(userId: _user.id!)
: {};
Map<String, String> renamedTeachers =
(await _database.query.getSettings(_database)).renamedTeachersEnabled
? await _database.userQuery.renamedTeachers(userId: _user.id!)
: {};
// v5
Map<String, String> customRoundings =
await _database.userQuery.getRoundings(userId: _user.user!.id);
for (Lesson lesson in lessons.values.expand((e) => e)) {
lesson.subject.renamedTo = renamedSubjects.isNotEmpty
? renamedSubjects[lesson.subject.id]
: null;
lesson.teacher.renamedTo = renamedTeachers.isNotEmpty
? renamedTeachers[lesson.teacher.id]
: null;
// v5
lesson.subject.customRounding = customRoundings.isNotEmpty
? double.parse(customRoundings[lesson.subject.id] ?? '5.0')
: null;
}
notifyListeners();
}
List<Lesson>? getWeek(Week week) => lessons[week];
// Fetches Lessons from the Kreta API then stores them in the database
Future<void> fetch({Week? week}) async {
if (week == null) return;
User? user = _user.user;
if (user == null) throw "Cannot fetch Lessons for User null";
String iss = user.instituteCode;
List? lessonsJson;
try {
lessonsJson = await _kreta
.getAPI(KretaAPI.timetable(iss, start: week.start, end: week.end));
} catch (e) {
lessonsJson = null;
}
if (lessonsJson == null) {
if (kDebugMode) print('Cannot fetch Lessons for User ${user.id}');
return;
// throw "Cannot fetch Lessons for User ${user.id}";
} else {
List<Lesson> lessonsList =
lessonsJson.map((e) => Lesson.fromJson(e)).toList();
lessons[week] = lessonsList;
await store();
await convertBySettings();
}
}
// Stores Lessons in the database
Future<void> store() async {
User? user = _user.user;
if (user == null) throw "Cannot store Lessons for User null";
String userId = user.id;
// -TODO: clear indexes with weeks outside of the current school year
await _database.userStore.storeLessons(lessons, userId: userId);
}
// Future<void> setLessonCount(SubjectLessonCount lessonCount, {bool store = true}) async {
// _subjectLessonCount = lessonCount;
// if (store) {
// User? user = Provider.of<UserProvider>(_context, listen: false).user;
// if (user == null) throw "Cannot store Lesson Count for User null";
// String userId = user.id;
// await Provider.of<DatabaseProvider>(_context, listen: false).userStore.storeSubjectLessonCount(lessonCount, userId: userId);
// }
// notifyListeners();
// }
}