Compare commits
2 Commits
4100a51194
...
5672be582a
Author | SHA1 | Date | |
---|---|---|---|
|
5672be582a | ||
|
379112a677 |
@ -10,6 +10,10 @@
|
|||||||
<DockerfileContext>.</DockerfileContext>
|
<DockerfileContext>.</DockerfileContext>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<_ContentIncludedByDefault Remove="appsettings.json" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.402.4" />
|
<PackageReference Include="AWSSDK.S3" Version="3.7.402.4" />
|
||||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||||
|
1
Mobile/.env
Normal file
1
Mobile/.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
OPENAI_API_KEY=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
|
3
Mobile/.gitignore
vendored
3
Mobile/.gitignore
vendored
@ -43,5 +43,4 @@ app.*.map.json
|
|||||||
/android/app/release
|
/android/app/release
|
||||||
|
|
||||||
environment.json
|
environment.json
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
|
@ -106,7 +106,7 @@ class _SideMenuState extends State<SideMenu> {
|
|||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('GuideBook'),
|
title: const Text('GuideBook'),
|
||||||
leading: const Icon(Icons.person),
|
leading: const Icon(Icons.menu_book_sharp),
|
||||||
selected: _selectedIndex == 5,
|
selected: _selectedIndex == 5,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.pushReplacementNamed(context, '/tourist-guide-book');
|
Navigator.pushReplacementNamed(context, '/tourist-guide-book');
|
||||||
@ -126,6 +126,19 @@ class _SideMenuState extends State<SideMenu> {
|
|||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text('GuideBook'),
|
||||||
|
leading: const Icon(Icons.menu_book_sharp),
|
||||||
|
selected: _selectedIndex == 5,
|
||||||
|
onTap: () {
|
||||||
|
Navigator.pushReplacementNamed(context, '/tourist-guide-book');
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const Divider(
|
||||||
|
color: Colors.grey,
|
||||||
|
thickness: 2,
|
||||||
|
indent: 40,
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Register'),
|
title: const Text('Register'),
|
||||||
leading: const Icon(Icons.add_box_outlined),
|
leading: const Icon(Icons.add_box_outlined),
|
||||||
|
@ -15,11 +15,12 @@ import 'base/sidemenu.dart';
|
|||||||
import 'profile.dart';
|
import 'profile.dart';
|
||||||
import 'models.dart';
|
import 'models.dart';
|
||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'services/api.dart' as api;
|
import 'services/api.dart' as api;
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
// Refresh JWT on startup
|
await dotenv.load(fileName: ".env");
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
if (prefs.getString("token") != null && prefs.getString("refresh-token") != null) {
|
if (prefs.getString("token") != null && prefs.getString("refresh-token") != null) {
|
||||||
final token = await api.request(null, api.ApiService.auth, "POST", "/RefreshToken", {'refreshToken': prefs.getString("refresh-token")});
|
final token = await api.request(null, api.ApiService.auth, "POST", "/RefreshToken", {'refreshToken': prefs.getString("refresh-token")});
|
||||||
|
@ -3,7 +3,7 @@ import 'dart:convert';
|
|||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
|
||||||
class OpenAIService {
|
class OpenAIService {
|
||||||
static Future<Map<String, dynamic>> getGuideBook(String country) async {
|
static Future<String> getGuideBook(String country) async {
|
||||||
final apiKey = dotenv.env['OPENAI_API_KEY'];
|
final apiKey = dotenv.env['OPENAI_API_KEY'];
|
||||||
final uri = Uri.parse('https://api.openai.com/v1/chat/completions');
|
final uri = Uri.parse('https://api.openai.com/v1/chat/completions');
|
||||||
|
|
||||||
@ -16,9 +16,10 @@ class OpenAIService {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": "Make a tourist guide book over $country. The topics should be: A little introduction to how the people of the country is like and what to expect from the environment, famous tourist attractions in the country(amusement park always included), Basic words that would be nice to learn in the country's language(should always be from English to the country's language) and transportation. These topics should be in a Json-Object format."
|
"content": "Make a tourist guide book over $country. The topics should be: A little introduction to how the people of the country is like and what to expect from the environment, famous tourist attractions in the country(amusement park always included), Basic words that would be nice to learn in the country's language(should always be from English to the country's language) and transportation. These topics should be in a Json-Object format in this form: {Country: {introduction: {people: , environment: }, tourist_attractions: {famous_sites: [{name: , description: }]}, basic_words: {}, transportation: {overview: , public_transport: {buses: , trains: , taxis: }}}}"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"max_tokens": 1500
|
||||||
});
|
});
|
||||||
|
|
||||||
final response = await http.post(
|
final response = await http.post(
|
||||||
@ -30,20 +31,25 @@ class OpenAIService {
|
|||||||
body: requestBody,
|
body: requestBody,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
|
final decodedBody = utf8.decode(response.bodyBytes);
|
||||||
|
final data = jsonDecode(decodedBody);
|
||||||
|
var content = data['choices'][0]['message']['content'];
|
||||||
|
|
||||||
try {
|
content = content.replaceAll('```json', '').replaceAll('```', '').trim();
|
||||||
return jsonDecode(response.body);
|
|
||||||
} catch (e) {
|
return _extractDescription(content);
|
||||||
return {
|
|
||||||
'navn': 'Fejl',
|
|
||||||
'art': 'Ukendt',
|
|
||||||
'type': 'Fejl',
|
|
||||||
'description': 'Fejl ved JSON parsing: $e'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw Exception('Fejl ved hentning af data: ${response.statusCode}');
|
throw Exception('Fejl ved billedanalyse: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static String _extractDescription(String content) {
|
||||||
|
final lines = content.split('\n');
|
||||||
|
final startIndex = lines.indexWhere((line) => line.startsWith('**Beskrivelse:**'));
|
||||||
|
|
||||||
|
if (startIndex == -1) return content;
|
||||||
|
|
||||||
|
return lines.skip(startIndex).join('\n').trim();
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,12 +1,8 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'package:dropdown_search/dropdown_search.dart';
|
import 'package:dropdown_search/dropdown_search.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:mobile/base/sidemenu.dart';
|
import 'package:mobile/base/sidemenu.dart';
|
||||||
import 'package:mobile/models.dart' as models;
|
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
|
||||||
import 'dart:convert';
|
|
||||||
import 'services/openaiservice.dart' as api;
|
import 'services/openaiservice.dart' as api;
|
||||||
import 'base/variables.dart';
|
|
||||||
|
|
||||||
class TouristGuideBookPage extends StatefulWidget {
|
class TouristGuideBookPage extends StatefulWidget {
|
||||||
const TouristGuideBookPage({super.key});
|
const TouristGuideBookPage({super.key});
|
||||||
@ -17,15 +13,66 @@ class TouristGuideBookPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _TouristGuideBookPageState extends State<TouristGuideBookPage> {
|
class _TouristGuideBookPageState extends State<TouristGuideBookPage> {
|
||||||
String? _selectedCountry;
|
String? _selectedCountry;
|
||||||
final List<String> countries = [
|
bool _isLoading = false;
|
||||||
"Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina",
|
Map<String, dynamic> _touristbook = {};
|
||||||
// Add more countries
|
final List<String> countries = [
|
||||||
];
|
"Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina",
|
||||||
|
"Armenia", "Australia", "Austria", "Azerbaijan", "Bahamas", "Bahrain",
|
||||||
|
"Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
|
||||||
|
"Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Brazil",
|
||||||
|
"Brunei", "Bulgaria", "Burkina Faso", "Burundi", "Cabo Verde", "Cambodia",
|
||||||
|
"Cameroon", "Canada", "Central African Republic", "Chad", "Chile", "China",
|
||||||
|
"Colombia", "Comoros", "Congo (Congo-Brazzaville)", "Congo (Democratic Republic)",
|
||||||
|
"Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark",
|
||||||
|
"Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt", "El Salvador",
|
||||||
|
"Equatorial Guinea", "Eritrea", "Estonia", "Eswatini", "Ethiopia", "Fiji",
|
||||||
|
"Finland", "France", "Gabon", "Gambia", "Georgia", "Germany", "Ghana",
|
||||||
|
"Greece", "Grenada", "Guatemala", "Guinea", "Guinea-Bissau", "Guyana",
|
||||||
|
"Haiti", "Honduras", "Hungary", "Iceland", "India", "Indonesia", "Iran",
|
||||||
|
"Iraq", "Ireland", "Israel", "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan",
|
||||||
|
"Kenya", "Kiribati", "Kuwait", "Kyrgyzstan", "Laos", "Latvia", "Lebanon",
|
||||||
|
"Lesotho", "Liberia", "Libya", "Liechtenstein", "Lithuania", "Luxembourg",
|
||||||
|
"Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
|
||||||
|
"Mauritania", "Mauritius", "Mexico", "Micronesia", "Moldova", "Monaco",
|
||||||
|
"Mongolia", "Montenegro", "Morocco", "Mozambique", "Myanmar", "Namibia",
|
||||||
|
"Nauru", "Nepal", "Netherlands", "New Zealand", "Nicaragua", "Niger",
|
||||||
|
"Nigeria", "North Korea", "North Macedonia", "Norway", "Oman", "Pakistan",
|
||||||
|
"Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
|
||||||
|
"Poland", "Portugal", "Qatar", "Romania", "Russia", "Rwanda", "Saint Kitts and Nevis",
|
||||||
|
"Saint Lucia", "Saint Vincent and the Grenadines", "Samoa", "San Marino",
|
||||||
|
"Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles",
|
||||||
|
"Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon Islands",
|
||||||
|
"Somalia", "South Africa", "South Korea", "South Sudan", "Spain", "Sri Lanka",
|
||||||
|
"Sudan", "Suriname", "Sweden", "Switzerland", "Syria", "Taiwan", "Tajikistan",
|
||||||
|
"Tanzania", "Thailand", "Timor-Leste", "Togo", "Tonga", "Trinidad and Tobago",
|
||||||
|
"Tunisia", "Turkey", "Turkmenistan", "Tuvalu", "Uganda", "Ukraine",
|
||||||
|
"United Arab Emirates", "United Kingdom", "United States", "Uruguay", "Uzbekistan",
|
||||||
|
"Vanuatu", "Vatican City", "Venezuela", "Vietnam", "Yemen", "Zambia", "Zimbabwe"
|
||||||
|
];
|
||||||
|
|
||||||
Future<void> getBook() async {
|
Future<void> getBook() async {
|
||||||
if (_selectedCountry != null) {
|
if (_selectedCountry != null) {
|
||||||
final response = await api.OpenAIService.getGuideBook(_selectedCountry!);
|
setState(() {
|
||||||
print(response); // For debugging to check the response
|
_isLoading = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await api.OpenAIService.getGuideBook(_selectedCountry!);
|
||||||
|
print(response);
|
||||||
|
final jsonData = jsonDecode(response);
|
||||||
|
setState(() {
|
||||||
|
_touristbook = jsonData[_selectedCountry!];
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
print('Error fetching guidebook: $e');
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text('Failed to load guidebook')),
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Choose a country')),
|
const SnackBar(content: Text('Choose a country')),
|
||||||
@ -33,54 +80,102 @@ class _TouristGuideBookPageState extends State<TouristGuideBookPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
Widget _buildTouristBookSection(String title, String content) {
|
||||||
Widget build(BuildContext context) {
|
return Padding(
|
||||||
return SideMenu(
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
selectedIndex: 5,
|
child: Column(
|
||||||
body: Scaffold(
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
appBar: AppBar(
|
children: [
|
||||||
title: Text('Select Country'),
|
Text(title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||||
),
|
SizedBox(height: 5),
|
||||||
body: Center(
|
Text(content, style: TextStyle(fontSize: 16)),
|
||||||
child: Padding(
|
],
|
||||||
padding: const EdgeInsets.all(16.0),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
DropdownSearch<String>(
|
|
||||||
items: countries,
|
|
||||||
popupProps: const PopupProps.menu(
|
|
||||||
showSearchBox: true,
|
|
||||||
searchFieldProps: TextFieldProps(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: "Search a country",
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
dropdownDecoratorProps: const DropDownDecoratorProps(
|
|
||||||
dropdownSearchDecoration: InputDecoration(
|
|
||||||
labelText: "Select a country",
|
|
||||||
hintText: "Search a country",
|
|
||||||
border: OutlineInputBorder(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
onChanged: (String? country) {
|
|
||||||
setState(() {
|
|
||||||
_selectedCountry = country;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
selectedItem: "Select a country you want a touristbook for",
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: getBook,
|
|
||||||
child: Text('Get Guidebook'),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SideMenu(
|
||||||
|
selectedIndex: 5,
|
||||||
|
body: Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text('Select Country'),
|
||||||
|
),
|
||||||
|
body: _isLoading
|
||||||
|
? Center(child: CircularProgressIndicator())
|
||||||
|
: SingleChildScrollView( // Added SingleChildScrollView here
|
||||||
|
padding: const EdgeInsets.all(16.0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
DropdownSearch<String>(
|
||||||
|
items: countries,
|
||||||
|
popupProps: const PopupProps.menu(
|
||||||
|
showSearchBox: true,
|
||||||
|
searchFieldProps: TextFieldProps(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Search a country",
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
dropdownDecoratorProps: const DropDownDecoratorProps(
|
||||||
|
dropdownSearchDecoration: InputDecoration(
|
||||||
|
labelText: "Select a country",
|
||||||
|
hintText: "Search a country",
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onChanged: (String? country) {
|
||||||
|
setState(() {
|
||||||
|
_selectedCountry = country;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
selectedItem: "Select a country you want a touristbook for",
|
||||||
|
),
|
||||||
|
SizedBox(height: 20),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: getBook,
|
||||||
|
child: Text('Get Guidebook'),
|
||||||
|
),
|
||||||
|
if (_touristbook.isNotEmpty) ...[
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Introduction - People",
|
||||||
|
_touristbook['introduction']?['people'] ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Introduction - Environment",
|
||||||
|
_touristbook['introduction']?['environment'] ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Famous Sites",
|
||||||
|
_touristbook['tourist_attractions']?['famous_sites']?.map((site) => site['name'] + ':\n' + site['description']).join('\n') ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Basic Language - Greetings",
|
||||||
|
_touristbook['basic_words']?.entries.map((e) => '${e.key}: ${e.value}').join('\n') ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Transportation Overview",
|
||||||
|
_touristbook['transportation']?['overview'] ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Public Transport - Buses",
|
||||||
|
_touristbook['transportation']?['public_transport']?['buses'] ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Public Transport - Trains",
|
||||||
|
_touristbook['transportation']?['public_transport']?['trains'] ?? 'No data',
|
||||||
|
),
|
||||||
|
_buildTouristBookSection(
|
||||||
|
"Public Transport - Taxis",
|
||||||
|
_touristbook['transportation']?['public_transport']?['taxis'] ?? 'No data',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -64,6 +64,7 @@ dev_dependencies:
|
|||||||
flutter:
|
flutter:
|
||||||
assets:
|
assets:
|
||||||
- assets/
|
- assets/
|
||||||
|
- .env
|
||||||
uses-material-design: true
|
uses-material-design: true
|
||||||
|
|
||||||
# To add assets to your application, add an assets section, like this:
|
# To add assets to your application, add an assets section, like this:
|
||||||
|
Loading…
Reference in New Issue
Block a user