Vorarbeit: WebUntis-API
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace LehrerApp.Api;
|
||||
|
||||
public static class WebUntisStudentReportParser
|
||||
{
|
||||
public static IReadOnlyList<UntisStudent> Parse(string content)
|
||||
{
|
||||
var rows = ParseSeparatedRows(content, '\t');
|
||||
if (rows.Count == 0) return [];
|
||||
|
||||
var headers = rows[0]
|
||||
.Select((header, index) => (index == 0 ? header.TrimStart('\uFEFF') : header).Trim())
|
||||
.ToArray();
|
||||
var result = new List<UntisStudent>();
|
||||
|
||||
foreach (var row in rows.Skip(1))
|
||||
{
|
||||
if (row.All(string.IsNullOrWhiteSpace)) continue;
|
||||
|
||||
var values = new Dictionary<string, string?>(StringComparer.Ordinal);
|
||||
for (var index = 0; index < headers.Length; index++)
|
||||
values[headers[index]] = index < row.Count ? row[index].Trim() : null;
|
||||
|
||||
var untisId = RequiredInt(Get(values, "id"), "id");
|
||||
var externalKey = RequiredInt(Get(values, "externKey"), "externKey");
|
||||
var name = Optional(Get(values, "name"));
|
||||
var lastName = Optional(Get(values, "longName"));
|
||||
var firstName = Optional(Get(values, "foreName"));
|
||||
var displayName = string.Join(' ', new[] { firstName, lastName }.Where(value => value is not null));
|
||||
if (string.IsNullOrWhiteSpace(displayName)) displayName = name ?? $"Schüler {untisId}";
|
||||
|
||||
result.Add(new UntisStudent(
|
||||
untisId,
|
||||
externalKey,
|
||||
Get(values, "klasse.name")?.Trim() ?? "",
|
||||
name,
|
||||
lastName,
|
||||
firstName,
|
||||
displayName,
|
||||
Optional(Get(values, "gender")),
|
||||
GermanDate(Get(values, "birthDate")),
|
||||
Optional(Get(values, "birthDate")),
|
||||
GermanDate(Get(values, "entryDate")),
|
||||
Optional(Get(values, "entryDate")),
|
||||
GermanDate(Get(values, "exitDate")),
|
||||
Optional(Get(values, "exitDate")),
|
||||
Optional(Get(values, "text")),
|
||||
Optional(Get(values, "medicalReportDuty")),
|
||||
Optional(Get(values, "schulpflicht")),
|
||||
Optional(Get(values, "majority")),
|
||||
new UntisStudentAddress(
|
||||
Optional(Get(values, "adress.email")),
|
||||
Optional(Get(values, "adress.mobile")),
|
||||
Optional(Get(values, "adress.phone")),
|
||||
Optional(Get(values, "adress.city")),
|
||||
Optional(Get(values, "adress.postCode")),
|
||||
Optional(Get(values, "adress.street"))),
|
||||
Optional(Get(values, "attribute.iL"))));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<List<string>> ParseSeparatedRows(string content, char separator)
|
||||
{
|
||||
var rows = new List<List<string>>();
|
||||
var row = new List<string>();
|
||||
var field = new StringBuilder();
|
||||
var inQuotes = false;
|
||||
|
||||
for (var index = 0; index < content.Length; index++)
|
||||
{
|
||||
var character = content[index];
|
||||
if (character == '"')
|
||||
{
|
||||
if (inQuotes && index + 1 < content.Length && content[index + 1] == '"')
|
||||
{
|
||||
field.Append('"');
|
||||
index++;
|
||||
}
|
||||
else
|
||||
{
|
||||
inQuotes = !inQuotes;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inQuotes && character == separator)
|
||||
{
|
||||
row.Add(field.ToString());
|
||||
field.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inQuotes && character == '\n')
|
||||
{
|
||||
row.Add(field.ToString());
|
||||
rows.Add(row);
|
||||
row = [];
|
||||
field.Clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inQuotes && character == '\r') continue;
|
||||
field.Append(character);
|
||||
}
|
||||
|
||||
row.Add(field.ToString());
|
||||
if (row.Count > 1 || !string.IsNullOrWhiteSpace(row[0])) rows.Add(row);
|
||||
return rows;
|
||||
}
|
||||
|
||||
private static string? Get(IReadOnlyDictionary<string, string?> values, string key) =>
|
||||
values.TryGetValue(key, out var value) ? value : null;
|
||||
|
||||
private static string? Optional(string? value) =>
|
||||
string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
||||
|
||||
private static int RequiredInt(string? value, string field) =>
|
||||
int.TryParse(value?.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var parsed)
|
||||
? parsed
|
||||
: throw new InvalidDataException($"Ungültige Zahl in Spalte \"{field}\".");
|
||||
|
||||
private static int? GermanDate(string? value)
|
||||
{
|
||||
if (!DateOnly.TryParseExact(value?.Trim(), "dd.MM.yyyy", CultureInfo.InvariantCulture,
|
||||
DateTimeStyles.None, out var date))
|
||||
return null;
|
||||
return date.Year * 10_000 + date.Month * 100 + date.Day;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user