115 lines
3.9 KiB
Dart
115 lines
3.9 KiB
Dart
import 'package:didvan/models/content/content_item.dart';
|
||
|
||
class SwotItem {
|
||
final int id;
|
||
final String title;
|
||
final String description;
|
||
final String category;
|
||
final String type;
|
||
final String imageUrl;
|
||
final double x1;
|
||
final double y1;
|
||
final String createdAt;
|
||
|
||
/// uuid اصلی content-service (در محتوای جدید). در دادهی قدیمی null است.
|
||
final String? keycloakId;
|
||
|
||
SwotItem({
|
||
required this.id,
|
||
required this.title,
|
||
required this.description,
|
||
required this.category,
|
||
required this.type,
|
||
required this.imageUrl,
|
||
required this.x1,
|
||
required this.y1,
|
||
required this.createdAt,
|
||
this.keycloakId,
|
||
});
|
||
|
||
factory SwotItem.fromJson(Map<String, dynamic> json) {
|
||
return SwotItem(
|
||
id: json['id'],
|
||
title: json['title'],
|
||
description: json['description'],
|
||
category: json['category'],
|
||
type: json['type'],
|
||
imageUrl: json["url"],
|
||
x1: json["scoreX1"],
|
||
y1: json["scoreY1"],
|
||
createdAt: json["createdAt"]
|
||
);
|
||
}
|
||
|
||
/// ساختن SwotItem از ContentItem (دستهی «فرصت و تهدید» در content-service).
|
||
/// نوع (opportunity/threat) از `metadata.type` میآید.
|
||
/// امتیازهای محور X و Y مستقیما از `metadata.x1`/`metadata.y1` خوانده
|
||
/// میشوند (افق ۱ ساله — وزندار شده در swot-service). اگر متادیتا خالی بود
|
||
/// (محتوای legacy)، به parse جدول قدیمی fallback میشود.
|
||
factory SwotItem.fromContentItem(ContentItem item) {
|
||
// widget قدیمی با type == "THREAT" (uppercase) چک میکند.
|
||
final isThreat = (item.metaType ?? '').toLowerCase() == 'threat';
|
||
final type = isThreat ? 'THREAT' : 'OPPORTUNITY';
|
||
|
||
double x1 = item.metaScoreX1;
|
||
double y1 = item.metaScoreY1;
|
||
if (x1 == 0 && y1 == 0) {
|
||
final fallback = _extractScores(item.body);
|
||
x1 = fallback.$1;
|
||
y1 = fallback.$2;
|
||
}
|
||
|
||
return SwotItem(
|
||
id: item.id.hashCode,
|
||
keycloakId: item.id,
|
||
title: item.title,
|
||
description: item.summary ?? '',
|
||
category: item.category?.name ?? 'فرصت و تهدید',
|
||
type: type,
|
||
imageUrl: item.coverImage ?? '',
|
||
x1: x1,
|
||
y1: y1,
|
||
createdAt: item.publishedAt?.toIso8601String() ?? '',
|
||
);
|
||
}
|
||
|
||
/// در body یک بلوک table پیدا میکند و میانگینِ ستونِ آخر را برای محورهای
|
||
/// X و Y برمیگرداند. اگر چیزی پیدا نشد، 0 برمیگرداند.
|
||
static (double, double) _extractScores(dynamic body) {
|
||
try {
|
||
if (body is! Map) return (0.0, 0.0);
|
||
final blocks = body['blocks'];
|
||
if (blocks is! List) return (0.0, 0.0);
|
||
for (final block in blocks) {
|
||
if (block is! Map) continue;
|
||
if (block['type'] != 'table') continue;
|
||
final data = block['data'];
|
||
if (data is! Map) continue;
|
||
final content = data['content'];
|
||
if (content is! List || content.isEmpty) continue;
|
||
// ستونِ آخر = آخرین مقدار در آرایهی هر سطر.
|
||
double xSum = 0, ySum = 0;
|
||
int xCount = 0, yCount = 0;
|
||
for (var i = 1; i < content.length; i++) {
|
||
final row = content[i];
|
||
if (row is! List || row.length < 3) continue;
|
||
final axis = row[1]?.toString().toUpperCase().trim();
|
||
final last = row.last?.toString().trim();
|
||
final val = double.tryParse(last ?? '');
|
||
if (val == null) continue;
|
||
if (axis == 'X') {
|
||
xSum += val;
|
||
xCount++;
|
||
} else if (axis == 'Y') {
|
||
ySum += val;
|
||
yCount++;
|
||
}
|
||
}
|
||
final x1 = xCount > 0 ? xSum / xCount : 0.0;
|
||
final y1 = yCount > 0 ? ySum / yCount : 0.0;
|
||
return (x1, y1);
|
||
}
|
||
} catch (_) {}
|
||
return (0.0, 0.0);
|
||
}
|
||
} |