cyberhybridhub/server/lib/pipeline/external_data_fetcher.dart

112 lines
3.3 KiB
Dart

import 'dart:convert';
import 'dart:math';
import 'package:http/http.dart' as http;
/// Fetches public web API data used to build question text and answers.
class ExternalDataFetcher {
ExternalDataFetcher({http.Client? client}) : _client = client ?? http.Client();
final http.Client _client;
final Random _random = Random();
/// Random country with population (millions) and capital for trivia questions.
Future<CountryFacts?> fetchRandomCountry() async {
final http.Response response = await _client.get(
Uri.parse(
'https://restcountries.com/v3.1/all?fields=name,population,capital',
),
);
if (response.statusCode != 200) {
return null;
}
final List<dynamic> countries =
jsonDecode(response.body) as List<dynamic>;
if (countries.isEmpty) {
return null;
}
for (int attempt = 0; attempt < 8; attempt++) {
final Map<String, dynamic> raw =
countries[_random.nextInt(countries.length)] as Map<String, dynamic>;
final int? population = raw['population'] as int?;
final List<dynamic>? capitals = raw['capital'] as List<dynamic>?;
final Map<String, dynamic>? nameMap =
raw['name'] as Map<String, dynamic>?;
final String? commonName = nameMap?['common'] as String?;
if (population == null ||
population < 500_000 ||
capitals == null ||
capitals.isEmpty ||
commonName == null) {
continue;
}
return CountryFacts(
name: commonName,
populationMillions: (population / 1_000_000).round(),
capital: capitals.first as String,
);
}
return null;
}
/// Current temperature (°C, rounded) for a European city via Open-Meteo.
Future<WeatherFacts?> fetchRandomCityWeather() async {
const List<({String city, double lat, double lon})> cities =
<({String city, double lat, double lon})>[
(city: 'Berlin', lat: 52.52, lon: 13.41),
(city: 'Paris', lat: 48.86, lon: 2.35),
(city: 'Madrid', lat: 40.42, lon: -3.70),
(city: 'Rome', lat: 41.90, lon: 12.50),
(city: 'Amsterdam', lat: 52.37, lon: 4.90),
];
final ({String city, double lat, double lon}) pick =
cities[_random.nextInt(cities.length)];
final Uri uri = Uri.parse(
'https://api.open-meteo.com/v1/forecast'
'?latitude=${pick.lat}&longitude=${pick.lon}'
'&current=temperature_2m&timezone=auto',
);
final http.Response response = await _client.get(uri);
if (response.statusCode != 200) {
return null;
}
final Map<String, dynamic> json =
jsonDecode(response.body) as Map<String, dynamic>;
final Map<String, dynamic>? current =
json['current'] as Map<String, dynamic>?;
final num? temp = current?['temperature_2m'] as num?;
if (temp == null) {
return null;
}
return WeatherFacts(
city: pick.city,
temperatureCelsius: temp.round(),
);
}
void close() => _client.close();
}
class CountryFacts {
CountryFacts({
required this.name,
required this.populationMillions,
required this.capital,
});
final String name;
final int populationMillions;
final String capital;
}
class WeatherFacts {
WeatherFacts({
required this.city,
required this.temperatureCelsius,
});
final String city;
final int temperatureCelsius;
}