feat: complete academic symbol lexicon heal, 2-column Cooper extraction, and updated assets
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart' show rootBundle;
|
||||
|
||||
import '../models/tradition_model.dart';
|
||||
|
||||
class TraditionsRepository {
|
||||
static final TraditionsRepository _instance = TraditionsRepository._internal();
|
||||
factory TraditionsRepository() => _instance;
|
||||
TraditionsRepository._internal();
|
||||
|
||||
List<TraditionCanon> _traditions = const [];
|
||||
bool _loaded = false;
|
||||
|
||||
List<TraditionCanon> get traditions => List.unmodifiable(_traditions);
|
||||
bool get isLoaded => _loaded;
|
||||
|
||||
static const Map<String, String> filterToCanonId = {
|
||||
'ᚱ Руны': 'runes',
|
||||
'᚛ Огам': 'ogham',
|
||||
'✡️ Каббала': 'kabbalah',
|
||||
'⚗️ Алхимия': 'alchemy',
|
||||
'🎴 Таро': 'tarot',
|
||||
'🪶 Ленорман': 'lenormand',
|
||||
'☯️ И Цзин': 'iching',
|
||||
'⌛ Геомантия': 'geomancy',
|
||||
'♈ Гороскоп': 'western_zodiac',
|
||||
'🐉 Китайский зодиак': 'zodiac',
|
||||
'🌌 Космология': 'cosmology',
|
||||
'📅 Дни недели': 'days_of_week',
|
||||
'🗓️ Месяцы года': 'months_of_year',
|
||||
'🔢 Нумерология': 'numerology',
|
||||
};
|
||||
|
||||
TraditionCanon? byId(String id) {
|
||||
for (final t in _traditions) {
|
||||
if (t.id == id) return t;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Finds all stations across traditions that relate to this word (either as primary station or in cluster)
|
||||
List<({TraditionCanon tradition, TraditionStation station, bool isPrimary})> findMatchesForWord(String word, {int? wordId}) {
|
||||
final cleanWord = word.trim().toLowerCase();
|
||||
final results = <({TraditionCanon tradition, TraditionStation station, bool isPrimary})>[];
|
||||
|
||||
for (final tradition in _traditions) {
|
||||
for (final station in tradition.stations) {
|
||||
if (station.primary != null &&
|
||||
(station.primary!.word.trim().toLowerCase() == cleanWord ||
|
||||
(wordId != null && station.primary!.id == wordId))) {
|
||||
results.add((tradition: tradition, station: station, isPrimary: true));
|
||||
} else if (station.cluster.any((cw) =>
|
||||
cw.word.trim().toLowerCase() == cleanWord ||
|
||||
(wordId != null && cw.id == wordId))) {
|
||||
results.add((tradition: tradition, station: station, isPrimary: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
Future<void> init() async {
|
||||
if (_loaded && _traditions.isNotEmpty) return;
|
||||
Map<String, dynamic>? decoded;
|
||||
|
||||
final cwd = Directory.current.path;
|
||||
final candidates = <String>{
|
||||
'assets/data/traditions_canon.json',
|
||||
'../assets/data/traditions_canon.json',
|
||||
'$cwd/assets/data/traditions_canon.json',
|
||||
'$cwd/../assets/data/traditions_canon.json',
|
||||
};
|
||||
|
||||
for (final path in candidates) {
|
||||
try {
|
||||
final file = File(path);
|
||||
if (file.existsSync()) {
|
||||
decoded = json.decode(file.readAsStringSync()) as Map<String, dynamic>;
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('traditions_canon.json skip $path: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (decoded == null) {
|
||||
try {
|
||||
final jsonStr = await rootBundle.loadString('assets/data/traditions_canon.json');
|
||||
decoded = json.decode(jsonStr) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
debugPrint('Could not load traditions_canon.json: $e');
|
||||
}
|
||||
}
|
||||
|
||||
if (decoded == null) {
|
||||
_traditions = const [];
|
||||
_loaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_traditions = decoded.values
|
||||
.whereType<Map<String, dynamic>>()
|
||||
.map(TraditionCanon.fromMap)
|
||||
.toList();
|
||||
_loaded = true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user