Skip to content

Commit

Permalink
[ Add, Edit ] adapted the localization process method to the SSE from…
Browse files Browse the repository at this point in the history
… the server, aligned eerything for the mvp, exposed version 0.9.0
anasfik committed Oct 7, 2023
1 parent af596ef commit 9564485
Showing 9 changed files with 259 additions and 110 deletions.
Binary file modified bin/langsync.exe
Binary file not shown.
3 changes: 2 additions & 1 deletion lib/src/command_runner.dart
Original file line number Diff line number Diff line change
@@ -13,11 +13,12 @@ import 'package:pub_updater/pub_updater.dart';

const executableName = 'langsync';
const packageName = 'langsync';
const description =
final description =
'''
An AI powered Command Line Interface (CLI) tool that helps you process your original language-specific files such translations, strings & texts.. and generates the corresponding translated files in the target language(s).
${utils.isDebugMode ? '\n${lightRed.wrap('Debug mode is enabled!')}' : ''}
''';

/// {@template langsync_command_runner}
59 changes: 39 additions & 20 deletions lib/src/commands/start_command/start_command.dart
Original file line number Diff line number Diff line change
@@ -94,8 +94,6 @@ class StartCommand extends Command<int> {
'Saving your source file at ${asConfig.sourceFile}..',
);

late Progress localizationProgress;

try {
final jsonPartitionRes = await NetClient.instance.savePartitionsJson(
apiKey: apiKey,
@@ -106,25 +104,15 @@ class StartCommand extends Command<int> {
.complete('Your source file has been saved successfully.');

logger
..info('\n')
..warn(
'The ID of this operation is: ${jsonPartitionRes.partitionId}. in case of any issues, please contact us providing this ID so we can help.',
)
// ..info("\n")
;

localizationProgress = logger.customProgress(
'Starting localization & translation to your target languages..',
);
..info('\n')
..warn(
'The ID of this operation is: ${jsonPartitionRes.partitionId}. in case of any issues, please contact us providing this ID so we can help.',
);

final result = await NetClient.instance.startAIProcess(
final result = await aIProcessResult(
apiKey: apiKey,
asConfig: asConfig,
jsonPartitionId: jsonPartitionRes.partitionId,
);

localizationProgress.complete(
'Localization operation is completed successfully.',
langs: asConfig.langs,
partitionId: jsonPartitionRes.partitionId,
);

logger
@@ -150,7 +138,7 @@ class StartCommand extends Command<int> {
} catch (e, stacktrace) {
logger.customErr(
error: e,
progress: localizationProgress,
progress: savingSourceFileProgress,
update: 'Something went wrong, try again!',
);

@@ -258,4 +246,35 @@ class StartCommand extends Command<int> {
..info('\n')
..success('All files are created successfully.');
}

Future<LangSyncServerResultSSE> aIProcessResult({
required String apiKey,
required Iterable<String> langs,
required String partitionId,
}) async {
final completer = Completer<LangSyncServerResultSSE>();

final processStream = NetClient.instance.startAIProcess(
apiKey: apiKey,
langs: langs,
jsonPartitionId: partitionId,
);

LangSyncServerResultSSE? resultSSE;

processStream.listen(
(event) {
if (event is LangSyncServerResultSSE) {
resultSSE = event;
} else {
logger.info(event.message);
}
},
onDone: () {
completer.complete(resultSSE!);
},
);

return completer.future;
}
}
91 changes: 85 additions & 6 deletions lib/src/etc/models/result_locale.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,100 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
import 'dart:convert';

import 'package:equatable/equatable.dart';

typedef JsonContentMap = Map<String, dynamic>;

class LocalizationOutput extends Equatable {
enum LangSyncServerSSEType { info, warn, error, result }

class LangSyncServerSSE extends Equatable {
final String message;
final LangSyncServerSSEType type;
final int statusCode;
final DateTime date;

const LangSyncServerSSE({
required this.message,
required this.type,
required this.statusCode,
required this.date,
});
@override
List<Object?> get props => [
message,
type,
statusCode,
date,
];

factory LangSyncServerSSE.fromJson(Map<String, dynamic> res) {
final type = LangSyncServerSSEType.values.firstWhere(
(element) => element.name == res['type'] as String,
);

if (type == LangSyncServerSSEType.result) {
return LangSyncServerResultSSE.fromJson(res);
} else {
return LangSyncServerSSE(
message: res['message'] as String,
type: type,
statusCode: res['statusCode'] as int,
date: DateTime.parse(res['date'] as String),
);
}
}
}

class LangSyncServerResultSSE extends LangSyncServerSSE {
final String outputPartitionId;

const LocalizationOutput({
const LangSyncServerResultSSE({
required this.outputPartitionId,
required super.message,
required super.type,
required super.statusCode,
required super.date,
});

factory LocalizationOutput.fromJson(Map<String, dynamic> res) {
return LocalizationOutput(
outputPartitionId: res['partitionId'] as String,
factory LangSyncServerResultSSE.fromJson(Map<String, dynamic> res) {
final message = res['message'] as String;
final decoded = jsonDecode(message) as Map<String, dynamic>;

return LangSyncServerResultSSE(
outputPartitionId: decoded['partitionId'] as String,
message: message,
statusCode: res['statusCode'] as int,
type: // this is hardcoded, butsince we are sure that it is correct.
LangSyncServerSSEType.result,
date: DateTime.parse(
res['date'] as String,
),
);
}

@override
List<Object?> get props => [outputPartitionId];
List<Object?> get props => [
outputPartitionId,
super.message,
super.type,
super.statusCode,
super.date,
];
}

class LangSyncServerLoggerSSE extends LangSyncServerSSE {
const LangSyncServerLoggerSSE({
required super.date,
required super.message,
required super.statusCode,
required super.type,
});

@override
List<Object?> get props => [
super.date,
super.message,
super.statusCode,
super.type,
];
}
97 changes: 18 additions & 79 deletions lib/src/etc/networking/client.dart
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import 'dart:convert';
import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:langsync/src/etc/models/api_key_res.dart';
import 'package:langsync/src/etc/models/config.dart';
import 'package:langsync/src/etc/models/lang_output.dart';
import 'package:langsync/src/etc/models/partition.dart';
import 'package:langsync/src/etc/models/result_locale.dart';
import 'package:langsync/src/etc/models/user_info.dart';
import 'package:langsync/src/etc/utils.dart';
import 'package:langsync/src/etc/networking/client_boilerplate.dart';
import 'package:langsync/src/version.dart';

class NetClient {
class NetClient extends NetClientBoilerPlate {
NetClient._();

final client = HttpClient();
@@ -21,15 +19,15 @@ class NetClient {
static NetClient get instance => _instance;

Future<UserInfo> userInfo({required String apiKey}) {
return _makeRes<UserInfo>('user', 'GET', {
return makeRes<UserInfo>('user', 'GET', {
'Authorization': 'Bearer $apiKey',
}, {}, (res) {
return UserInfo.fromJson(res);
});
}

Future<Map<String, bool>> supportsLang(List<String> lang) {
return _makeRes<Map<String, bool>>('/langs-support', 'POST', {}, {
return makeRes<Map<String, bool>>('/langs-support', 'POST', {}, {
'langs': lang,
}, (res) {
final map = <String, bool>{};
@@ -44,93 +42,34 @@ class NetClient {
});
}

Future<T> _makeRes<T>(
String endpoint,
String method,
Map<String, String> headers,
Map<String, dynamic> body,
T Function(Map<String, dynamic> res) onSuccess,
) async {
final uri = Uri.parse(utils.endpoint(endpoint));

final request = http.Request(method, uri);

request.headers.addAll({
...headers,
'Content-Type': 'application/json',
});

// include body in request.

request.body = json.encode(body);

final res = await request.send();
final asBytes = await res.stream.bytesToString();

final decoded = jsonDecode(asBytes) as Map<String, dynamic>;

if (res.statusCode >= 200 && res.statusCode < 300) {
return onSuccess(decoded);
} else {
throw Exception(decoded);
}
}

Future<T> _makeMultiPartRes<T>(
String endpoint,
String method,
Map<String, String> headers,
Map<String, dynamic> body,
T Function(Map<String, dynamic> res) onSuccess,
) async {
final uri = Uri.parse(utils.endpoint(endpoint));
final request = http.MultipartRequest(method, uri);

request.headers.addAll({...headers});
body.forEach((key, value) {
if (value is File) {
final multipartFile = http.MultipartFile.fromBytes(
key,
value.readAsBytesSync(),
filename: value.path.split('/').last,
);

request.files.add(multipartFile);
} else {
request.fields[key] = value.toString();
}
});

final res = await request.send();
final asBytes = await res.stream.bytesToString();

return onSuccess(jsonDecode(asBytes) as Map<String, dynamic>);
}

Future<LocalizationOutput> startAIProcess({
required LangSyncConfig asConfig,
Stream<LangSyncServerSSE> startAIProcess({
required Iterable<String> langs,
required String apiKey,
required String jsonPartitionId,
bool includeOutput = false,
}) {
return _makeRes(
return sseStreamReq(
'/process-translation',
'POST',
{'Authorization': 'Bearer $apiKey'},
{
'jsonPartitionsId': jsonPartitionId,
'langs': asConfig.langs.toList(),
'langs': langs.toList(),
'includeOutput': includeOutput,
},
LocalizationOutput.fromJson,
(res) {
final decoded = jsonDecode(res) as Map<String, dynamic>;

return LangSyncServerSSE.fromJson(decoded);
},
);
}

Future<PartitionResponse> savePartitionsJson({
required String apiKey,
required File sourceFile,
}) {
return _makeMultiPartRes(
return makeMultiPartRes(
'/save-partitioned-json-of-user',
'post',
{'Authorization': 'Bearer $apiKey'},
@@ -142,7 +81,7 @@ class NetClient {
Future<bool> checkWetherApiKeyExistsForSomeUser({
required String apiKey,
}) async {
return _makeRes(
return makeRes(
'/verify-api-key-existence',
'GET',
{'Authorization': 'Bearer $apiKey'},
@@ -163,7 +102,7 @@ class NetClient {
Future<List<LangOutput>> retrieveJsonPartitionWithOutput({
required String outputPartitionId,
}) {
return _makeRes(
return makeRes(
'/get-partitioned-json-of-user',
'GET',
{},
@@ -183,7 +122,7 @@ class NetClient {
}

Future<APIKeyResponse> createApiKey(String userName) {
return _makeRes('/create-account-with-api-key-beta', 'POST', {}, {
return makeRes('/create-account-with-api-key-beta', 'POST', {}, {
'username': userName,
}, (res) {
return APIKeyResponse.fromJson(res);
@@ -196,7 +135,7 @@ class NetClient {
required String commandName,
String? processId,
}) {
return _makeRes(
return makeRes(
'/log-exception',
'POST',
{},
Loading

0 comments on commit 9564485

Please sign in to comment.