Skip to content

Commit

Permalink
added mergedblock #192
Browse files Browse the repository at this point in the history
  • Loading branch information
DevKevYT committed Dec 22, 2022
1 parent 85c60ad commit 65c4ffb
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 234 deletions.
27 changes: 0 additions & 27 deletions lib/core/api/readme.md

This file was deleted.

1 change: 1 addition & 0 deletions lib/core/api/usersession.dart
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class UserSession {
http.Response r = await _queryURL("/WebUntis/api/token/new");
if (r.statusCode == 200) {
_bearerToken = r.body;
log.d("Bearer Token refreshed");
} else {
log.w("Warning: Failed to fetch api token. Unable to call 'getNews()' and 'getProfileData()'");
}
Expand Down
21 changes: 21 additions & 0 deletions lib/core/excel/models/mergedblock.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:sol_connect/core/excel/models/mergedtimetable.dart';

///Anders als "mergedTimetable" beinhaltet diese Klasse einen kompletten Block, nicht nur eine Woche
class MergedBlock {

final _hours = <MergedTimeTable>[];

final DateTime _blockStart;
final DateTime _blockEnd;

MergedBlock(this._blockStart, this._blockEnd, List<MergedTimeTable> weeks) {
for (MergedTimeTable week in weeks) {
_hours.add(week); //TODO @DevKevYT further checks for valid weeks
}
}

DateTime get blockStart => _blockStart;

DateTime get blockEnd => _blockEnd;

}
6 changes: 1 addition & 5 deletions lib/core/excel/models/mergedtimetable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,6 @@ import 'package:sol_connect/core/excel/models/mappedsheet.dart';
import 'package:sol_connect/core/excel/validator.dart';

class MergedTimeTable {
///Gibt eine Fehlermeldung zurück, falls dieser Stundenplan nicht mit der Excel gemappt werden konnte.
///
///Immer leer, außer `verified() == false`
//String errorMessage = "";

//Der Stundenplan
final TimeTableRange timetable;
Expand All @@ -31,7 +27,7 @@ class MergedTimeTable {

///Gibt die entsprechende Phasierung zum Stunden Index zurück.
///
///* ` getPhaseFromHourIndex(xIndex: 0, yIndex: 0)` gibt die Phasierung zur ersten Stunde am Montag zurück
///* `getPhaseFromHourIndex(xIndex: 0, yIndex: 0)` gibt die Phasierung zur ersten Stunde am Montag zurück
///* `getPhaseFromHourIndex(xIndex: 2, yIndex: 2)` gibt die Phasierung zur dritten Stunde am Mittwoch zurück
///
///Gibt immer unbekannte Phasen zurück, wenn `verified() == false`
Expand Down
9 changes: 0 additions & 9 deletions lib/core/excel/readme.md

This file was deleted.

187 changes: 39 additions & 148 deletions lib/core/excel/solc_api_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,19 @@ import 'package:logger/logger.dart';
import 'package:sol_connect/core/api/models/utils.dart';
import 'package:sol_connect/core/api/usersession.dart';
import 'package:sol_connect/core/excel/models/cellcolors.dart';
import 'package:sol_connect/core/excel/models/mergedblock.dart';
import 'package:sol_connect/core/excel/models/phasestatus.dart';
import 'package:sol_connect/core/excel/models/version.dart';
import 'package:sol_connect/core/excel/solcresponse.dart';
import 'package:sol_connect/core/exceptions.dart';
import 'package:sol_connect/util/logger.util.dart';

//Vielleicht einen besseren Namen ausdenken
//Handled alles SOLC-API Server zeug

///Interface für SOLC-API-3 Server
class SOLCApiManager {
final Logger log = getLogger();

int _activeSockets = 0;

String _baseURL;
int _port;
static const int timeoutSeconds = 5; //Throw a timeout if a response does not come within x seconds
static const int timeoutSeconds =
5; //Throw a timeout if a response does not come within x seconds

static final Version buildRequired = Version.of("3.0.0");

Expand All @@ -45,7 +41,8 @@ class SOLCApiManager {
int get port => _port;

Future<Version> getVersion() async {
http.Response response = await http.Client().get(Uri.parse("$apiAddress/version"));
http.Response response =
await http.Client().get(Uri.parse("$apiAddress/version"));
if (response.statusCode == 200 && response.body.isNotEmpty) {
return Version.of(jsonDecode(response.body)['data']['displayValue']);
} else {
Expand All @@ -55,8 +52,10 @@ class SOLCApiManager {

///Sendet eine Anfrage an den Server, um Farben einer Excel Datei zu extrahieren.
Future<CellColors> getExcelColorData(List<int> fileBytes) async {
var request = http.MultipartRequest("POST", Uri.parse("$apiAddress/getcolor"));
request.files.add(http.MultipartFile.fromBytes('sheet', fileBytes, filename: 'sheet'));
var request =
http.MultipartRequest("POST", Uri.parse("$apiAddress/getcolor"));
request.files.add(
http.MultipartFile.fromBytes('sheet', fileBytes, filename: 'sheet'));

http.StreamedResponse response = await request.send();
if (response.statusCode == 200) {
Expand All @@ -67,24 +66,29 @@ class SOLCApiManager {
}

///Wirft eine Exception wenn ein Fehlercode auftritt
///@deprecated
Future<PhaseStatus?> getSchoolClassInfo({required int schoolClassId}) async {
SOLCResponse? response = await _querySOLC(command: "phase-status <$schoolClassId>");
if (response != null) {
return PhaseStatus(response.payload);
}
//SOLCResponse? response =
// await _querySOLC(command: "phase-status <$schoolClassId>");
//if (response != null) {
// return PhaseStatus(response.payload);
//}
return null;
}

Future<List<int>> downloadVirtualSheet({required int schoolClassId}) async {
List<int> bytes = [];
await _querySOLC(command: "download-file <$schoolClassId>", downloadBytes: bytes);
//await _querySOLC(
// command: "download-file <$schoolClassId>", downloadBytes: bytes);
return bytes;
}

///Wirft eine Exception wenn ein Fehlercode auftritt
Future<void> downloadSheet({required int schoolClassId, required File targetFile}) async {
Future<void> downloadSheet(
{required int schoolClassId, required File targetFile}) async {
List<int> bytes = [];
await _querySOLC(command: "download-file <$schoolClassId>", downloadBytes: bytes);
//await _querySOLC(
// command: "download-file <$schoolClassId>", downloadBytes: bytes);

await targetFile.create(recursive: true);
await targetFile.writeAsBytes(bytes);
Expand All @@ -93,137 +97,24 @@ class SOLCApiManager {
///Wirft eine Exception wenn ein Fehlercode auftritt
///
///**ACHTUNG! Bei erfolgreichem hochladen wird der user automatisch serverseitig abgemeldet!**
Future<void> uploadSheet(
Future<void> uploadPhasing(
{required UserSession authenticatedUser,
required int schoolClassId,
required DateTime blockStart,
required DateTime blockEnd,
required File file}) async {
await _querySOLC(
uploadFileSource: file,
command:
"upload-file <${authenticatedUser.sessionid}> <${authenticatedUser.bearerToken}> <$schoolClassId> <${Utils.convertToUntisDate(blockStart)}> <${Utils.convertToUntisDate(blockEnd)}>");
await authenticatedUser.regenerateSession();
}

///Transferdata ist eigentlich nur eine Datei
///Transferdata wird gebraucht, wenn ein Befehl einen Dateiupload oder Download inizialisiert.
///
///Zurückgegebene Werte können null sein, je nachdem was für ein Befehl benutzt wurde.
///Ein rückgabewert gibt es nur wenn eine Serverantwort im JSON Format kommt bzw wenn der Code 0 (SUCCESS) ist.
///Ansonsten wird alles über die File Objekte gehandled
///Spezielle Exceptions:
///* `SOLCServerError` Hat zusätzlich noch die ursprüngliche Nachricht
///
///Folgende "normale" Exceptions können geworfen werden:
///* `UploadFileNotSpecifiedException` Wenn ein Befehl ein Dateiupload erwartet diese jedoch nicht angegeben wurde
///* `UploadFileNotFoundException` Wenn die hochzuladene Datei im System nicht gefunden werden konnte bzw. nicht existiert
///* `DownloadFileNotFoundException` Wenn keine Datei zum Download nicht angegeben wurde
///
@Deprecated('Use [ConflictException]')
Future<SOLCResponse?> _querySOLC({required String command, File? uploadFileSource, List<int>? downloadBytes}) async {
SOLCResponse? returnValue;
dynamic exception;

try {
_activeSockets = _activeSockets + 1;
final socket = await Socket.connect(_baseURL, _port);

//Sende den Befehl
socket.writeln(command);
await socket.flush();

void throwException(Exception e) {
exception = e;
socket.close();
}

bool awaitFileStream = false;

if (downloadBytes != null) {
downloadBytes.clear();
}

var subscription = socket.listen(
(event) async {
if (awaitFileStream) {
if (downloadBytes == null) {
throwException(DownloadFileNotFoundException("Kein Ziel zum Download angegeben"));
return;
}
downloadBytes.addAll(event);
return;
}

dynamic decodedMessage = "";
try {
decodedMessage = jsonDecode(String.fromCharCodes(event));
} on FormatException {
socket.close();
return;
}

SOLCResponse response = SOLCResponse.handle(decodedMessage);
if (response.isError) {
throwException(
SOLCServerError("${response.errorMessage} (SOLC Error Code: ${response.responseCode})", response));
return;
}

//Einfache JSON Antwort
if (response.responseCode == SOLCResponse.CODE_SUCCESS) {
returnValue = response;
socket.close();
return;
}

//Server bereit einen Dateiupload zu empfangen
if (response.responseCode == SOLCResponse.CODE_READY) {
if (uploadFileSource == null) {
throwException(UploadFileNotSpecifiedException("Keine Datei zum Upload angegeben"));
socket.close();
return;
}
if (!(await uploadFileSource.exists())) {
throwException(UploadFileNotFoundException("Datei zum Upload existiert nicht"));
return;
}
await socket.addStream(uploadFileSource.openRead());
socket.close();
return;
}

//Server fragt den Client ob er bereit ist eine Datei zu downloaden. Sende ein "ready-to-recieve" und lade die Date als Stream herunter
if (response.responseCode == SOLCResponse.CODE_SEND_READY) {
awaitFileStream = true;
socket.writeln("ready-to-recieve");
await socket.flush();
return;
}
},
onError: (error) {
_activeSockets--;
throwException(Exception("Ein Fehler ist bei der Verbindung zum SOLC-API Server aufgetreten"));
},
);

await subscription.asFuture<void>().timeout(const Duration(seconds: timeoutSeconds), onTimeout: () {
throwException(SOLCServerResponseTimeoutException("Timeout after $timeoutSeconds seconds"));
});

await socket.close();
await subscription.cancel();

_activeSockets--;
log.d("Connection for SOLC command '$command' closed. $_activeSockets active connections.");
} on Exception catch (error) {
_activeSockets--;
throw FailedToEstablishSOLCServerConnection(
"Konnte keine Verbindung zum Konvertierungsserver $_baseURL herstellen: $error");
}
if (exception != null) {
throw exception;
}
return returnValue;
required MergedBlock block}) async {

///Wandelt dieses Objekt in eine PhTP gültige JSON um
var object = {
"version": "1",
"classId": schoolClassId,
"blockStart": Utils.convertToUntisDate(block.blockStart),
"blockEnd": Utils.convertToUntisDate(block.blockEnd)
};

http.Response response = await http.post(Uri.parse("$apiAddress/api/phasing/upload"),
headers: {
"JSESSIONID": authenticatedUser.sessionid,
"Authentication": "Bearer ${authenticatedUser.bearerToken}"
},
body: jsonEncode(object));
}
}
13 changes: 10 additions & 3 deletions lib/core/excel/validator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:sol_connect/core/api/timetable_manager.dart';
import 'package:sol_connect/core/api/usersession.dart';
import 'package:sol_connect/core/excel/models/cellcolors.dart';
import 'package:sol_connect/core/excel/models/mappedsheet.dart';
import 'package:sol_connect/core/excel/models/mergedblock.dart';
import 'package:sol_connect/core/excel/models/mergedtimetable.dart';
import 'package:sol_connect/core/excel/models/phaseelement.dart';
import 'package:sol_connect/core/excel/solc_api_manager.dart';
Expand Down Expand Up @@ -113,19 +114,25 @@ class ExcelValidator {
_collectedTimetables.add(mapped);
}

///Wenn keineException geworfen wurde ist der Merge erfolgreich gewesen.
Future<void> mergeExcelWithWholeBlock(UserSession session) async {
///Wenn keineException geworfen wurde und ein "MergedBlock" Objekt zurückgegeben wurde, war der merge erfolgreich
Future<MergedBlock> mergeExcelWithWholeBlock(UserSession session) async {
TimeTableRange timeTable = await session.getRelativeTimeTableWeek(0);
var nextBlockweeks =
await timeTable.getBoundFrame().getManager().getNextBlockWeeks();

List<MergedTimeTable> mergedWeeks = <MergedTimeTable>[];
for (TimetableFrame blockWeek in nextBlockweeks) {
log.d(
"Verifying block week phase merge ${blockWeek.getFrameStart()} -> ${blockWeek.getFrameEnd()}");

await blockWeek.getCurrentBlockWeek();
await mergeExcelWithTimetable(await blockWeek.getWeekData());
mergedWeeks
.add(await mergeExcelWithTimetable(await blockWeek.getWeekData()));
}

return MergedBlock(nextBlockweeks.first.getFrameStart(),
nextBlockweeks.last.getFrameEnd(),
mergedWeeks);
}

///Verifiziert die im Konstruktor angegebene Excel Datei und überprüft, ob der Stundenplan enthalten ist.
Expand Down
Loading

0 comments on commit 65c4ffb

Please sign in to comment.