Skip to content

Commit cdc3139

Browse files
fiy batchCreateUsers (WIP)
1 parent c4a90a9 commit cdc3139

4 files changed

Lines changed: 146 additions & 208 deletions

File tree

school_data_hub_client/lib/src/protocol/client.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ class EndpointAdminUser extends _i1.EndpointRef {
403403
);
404404

405405
/// Batch-creates users. Returns credentials for successes and errors for skipped/failed rows.
406-
/// Each create runs in its own transaction; creates are executed in parallel (up to 5 at a time) to reduce timeout risk.
406+
/// Each create runs in its own transaction; creates are executed in parallel (up to 5 at a time).
407+
/// Duplicates are detected by the DB (unique constraint); we catch 23505 and report a friendly message.
407408
_i2.Future<_i13.BatchCreateUsersResponse> batchCreateUsers(
408409
List<_i14.CreateUserRequest> requests) =>
409410
caller.callServerEndpoint<_i13.BatchCreateUsersResponse>(
@@ -412,7 +413,7 @@ class EndpointAdminUser extends _i1.EndpointRef {
412413
{'requests': requests},
413414
);
414415

415-
/// Streams batch create results one-by-one to avoid HTTP timeout. Same validation and create logic as [batchCreateUsers].
416+
/// Streams batch create results one-by-one to avoid HTTP timeout. Duplicates are detected by the DB; we catch 23505 and yield a friendly error event.
416417
_i2.Stream<_i15.BatchCreateUserEvent> batchCreateUsersStream(
417418
List<_i14.CreateUserRequest> requests) =>
418419
caller.callStreamingServerEndpoint<_i2.Stream<_i15.BatchCreateUserEvent>,

school_data_hub_flutter/lib/features/user/domain/user_manager.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/foundation.dart';
22
import 'package:flutter_it/flutter_it.dart';
3+
import 'package:logging/logging.dart';
34
import 'package:school_data_hub_client/school_data_hub_client.dart';
45
import 'package:school_data_hub_flutter/common/services/notification_service.dart';
56
import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart';
@@ -8,6 +9,8 @@ import 'package:school_data_hub_flutter/features/user/domain/batch_create_result
89
import 'package:school_data_hub_flutter/features/user/domain/staff_import_password_generator.dart';
910
import 'package:school_data_hub_flutter/features/user/domain/staff_import_row.dart';
1011

12+
final _log = Logger('UserManager');
13+
1114
/// Data class for createUser command parameters.
1215
typedef CreateUserParams = ({
1316
String userName,
@@ -284,6 +287,7 @@ class UserManager {
284287
rows,
285288
generatePassword: generatePassword,
286289
);
290+
_log.info('[UserManager] batchCreateUsersStreamFromImportRows: rows=${rows.length} -> requests=${requests.length}, calling API stream');
287291
return _apiService.batchCreateUsersStream(requests);
288292
}
289293

school_data_hub_flutter/lib/features/user/presentation/batch_import_users/batch_import_users_page.dart

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,21 @@ import 'dart:typed_data';
44
import 'package:flutter/material.dart';
55
import 'package:flutter_it/flutter_it.dart';
66
import 'package:gap/gap.dart';
7+
import 'package:logging/logging.dart';
78
import 'package:printing/printing.dart';
9+
import 'package:school_data_hub_client/school_data_hub_client.dart';
810
import 'package:school_data_hub_flutter/app_utils/pdf_viewer_page.dart';
911
import 'package:school_data_hub_flutter/common/theme/app_colors.dart';
1012
import 'package:school_data_hub_flutter/common/theme/styles.dart';
1113
import 'package:school_data_hub_flutter/common/widgets/bottom_nav_bar/generic_bottom_nav_bar.dart';
1214
import 'package:school_data_hub_flutter/common/widgets/generic_components/generic_app_bar.dart';
1315
import 'package:school_data_hub_flutter/features/user/data/staff_excel_import_parser.dart';
14-
import 'package:school_data_hub_client/school_data_hub_client.dart';
1516
import 'package:school_data_hub_flutter/features/user/domain/batch_create_result.dart';
1617
import 'package:school_data_hub_flutter/features/user/domain/user_manager.dart';
1718
import 'package:school_data_hub_flutter/features/user/presentation/batch_import_users/staff_credentials_pdf_service.dart';
1819

20+
final _log = Logger('BatchImportUsersPage');
21+
1922
class BatchImportUsersPage extends StatefulWidget {
2023
const BatchImportUsersPage({super.key});
2124

@@ -49,7 +52,11 @@ class _BatchImportUsersPageState extends State<BatchImportUsersPage> {
4952

5053
Future<void> _createUsers() async {
5154
final rows = _parseResult?.rows ?? [];
52-
if (rows.isEmpty) return;
55+
_log.info('[BatchImport] Benutzer anlegen clicked, rows=${rows.length}');
56+
if (rows.isEmpty) {
57+
_log.warning('[BatchImport] No rows to create, returning');
58+
return;
59+
}
5360
setState(() {
5461
_isCreating = true;
5562
_batchResult = null;
@@ -61,40 +68,58 @@ class _BatchImportUsersPageState extends State<BatchImportUsersPage> {
6168
final errors = <BatchCreateError>[];
6269

6370
try {
71+
_log.info('[BatchImport] Getting stream from UserManager');
6472
final stream = userManager.batchCreateUsersStreamFromImportRows(rows);
73+
_log.info('[BatchImport] Subscribing to batchCreateUsersStream');
6574
_streamSubscription = stream.listen(
6675
(event) {
6776
if (!mounted) return;
6877
if (event.credential != null) {
6978
final c = event.credential!;
70-
credentials.add(StaffCredentialEntry(
71-
userName: c.userName,
72-
fullName: c.fullName,
73-
email: c.email,
74-
password: c.password,
75-
));
79+
credentials.add(
80+
StaffCredentialEntry(
81+
userName: c.userName,
82+
fullName: c.fullName,
83+
email: c.email,
84+
password: c.password,
85+
),
86+
);
7687
setState(() => _progressCreated = credentials.length);
88+
_log.info(
89+
'[BatchImport] Event: created ${credentials.length} — ${c.userName}',
90+
);
7791
} else if (event.error != null) {
7892
final e = event.error!;
79-
errors.add(BatchCreateError(
80-
rowIndex: e.rowIndex,
81-
userNameOrKurzel: e.userNameOrKurzel,
82-
message: e.message,
83-
));
93+
errors.add(
94+
BatchCreateError(
95+
rowIndex: e.rowIndex,
96+
userNameOrKurzel: e.userNameOrKurzel,
97+
message: e.message,
98+
),
99+
);
84100
setState(() => _progressErrors = errors.length);
101+
_log.info(
102+
'[BatchImport] Event: error ${errors.length} — row ${e.rowIndex} ${e.userNameOrKurzel}: ${e.message}',
103+
);
85104
}
86105
},
87-
onError: (Object e) {
106+
onError: (Object e, StackTrace? st) {
107+
_log.severe('[BatchImport] Stream onError', e, st);
88108
if (mounted) {
89109
setState(() => _isCreating = false);
90-
ScaffoldMessenger.of(context).showSnackBar(
91-
SnackBar(content: Text('Fehler: $e')),
92-
);
110+
ScaffoldMessenger.of(
111+
context,
112+
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
93113
}
94114
},
95115
onDone: () async {
116+
_log.info(
117+
'[BatchImport] Stream onDone — credentials=${credentials.length} errors=${errors.length}',
118+
);
96119
if (!mounted) return;
120+
_log.info('[BatchImport] Refreshing user list');
97121
await userManager.fetchUsersCommand.runAsync();
122+
if (!mounted) return;
98123
setState(() {
99124
_batchResult = BatchCreateResult(
100125
credentials: credentials,
@@ -104,15 +129,19 @@ class _BatchImportUsersPageState extends State<BatchImportUsersPage> {
104129
_progressCreated = 0;
105130
_progressErrors = 0;
106131
});
132+
_log.info(
133+
'[BatchImport] Batch complete. Success: ${credentials.length}, failures: ${errors.length}',
134+
);
107135
},
108136
cancelOnError: false,
109137
);
110-
} catch (e) {
138+
} catch (e, st) {
139+
_log.severe('[BatchImport] _createUsers catch', e, st);
111140
if (mounted) {
112141
setState(() => _isCreating = false);
113-
ScaffoldMessenger.of(context).showSnackBar(
114-
SnackBar(content: Text('Fehler: $e')),
115-
);
142+
ScaffoldMessenger.of(
143+
context,
144+
).showSnackBar(SnackBar(content: Text('Fehler: $e')));
116145
}
117146
}
118147
}

0 commit comments

Comments
 (0)