diff --git a/.github/workflows/main_beakpeekbirdapi.yml b/.github/workflows/main_beakpeekbirdapi.yml deleted file mode 100644 index 35642e63..00000000 --- a/.github/workflows/main_beakpeekbirdapi.yml +++ /dev/null @@ -1,65 +0,0 @@ -# Docs for the Azure Web Apps Deploy action: https://github.com/Azure/webapps-deploy -# More GitHub Actions for Azure: https://github.com/Azure/actions - -name: Build and deploy ASP.Net Core app to Azure Web App - BeakPeekBirdApi - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up .NET Core - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.x' - - - name: Build with dotnet - run: dotnet build --configuration Release - - - name: dotnet publish - run: dotnet publish -c Release -o ${{env.DOTNET_ROOT}}/myapp - - - name: Upload artifact for deployment job - uses: actions/upload-artifact@v4 - with: - name: .net-app - path: ${{env.DOTNET_ROOT}}/myapp - - deploy: - runs-on: ubuntu-latest - needs: build - environment: - name: 'Production' - url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} - permissions: - id-token: write #This is required for requesting the JWT - - steps: - - name: Download artifact from build job - uses: actions/download-artifact@v4 - with: - name: .net-app - - - name: Login to Azure - uses: azure/login@v2 - with: - client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_F91BBE34AB4B45898BF82BFADF359BE8 }} - tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_F1EE328E09964C2EA0E92A6629C60647 }} - subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_97F61458BB2B43A89AB06261472988FE }} - - - name: Deploy to Azure Web App - id: deploy-to-webapp - uses: azure/webapps-deploy@v3 - with: - app-name: 'BeakPeekBirdApi' - slot-name: 'Production' - package: . - \ No newline at end of file diff --git a/beakpeek/assets/Legal/combinepdf.pdf b/beakpeek/assets/Legal/combinepdf.pdf new file mode 100644 index 00000000..6a672fa8 Binary files /dev/null and b/beakpeek/assets/Legal/combinepdf.pdf differ diff --git a/beakpeek/lib/Controller/DB/life_list_provider.dart b/beakpeek/lib/Controller/DB/life_list_provider.dart index ebc5dc57..4428f272 100644 --- a/beakpeek/lib/Controller/DB/life_list_provider.dart +++ b/beakpeek/lib/Controller/DB/life_list_provider.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:beakpeek/Model/BirdInfo/bird.dart'; import 'package:beakpeek/Model/BirdInfo/province_data.dart'; +import 'package:beakpeek/Model/UserProfile/achievment_list.dart'; import 'package:beakpeek/Model/UserProfile/user_model.dart'; import 'package:beakpeek/config_azure.dart'; import 'package:http/http.dart'; @@ -166,6 +167,8 @@ class LifeListProvider { Future insertBird(int birdId) async { final bird = await getBirdInByID(birdId); + print(bird); + await updateLifeListAchievments(bird.commonGroup, birdId); final db = await instance.database; if (!await isDuplicate(bird)) { await db @@ -196,6 +199,19 @@ class LifeListProvider { return encodedString; } + Future fetchUserLifelistString() async { + final db = await instance.database; + final List> birdMap = await db.query( + 'birds', + orderBy: 'commonGroup DESC', + columns: ['id'], + ); + final String encodedString = jsonEncode(birdMap); + // user.lifelist = encodedString; + // storeUserLocally(user); + return encodedString; + } + Future> fetchLifeList() async { final db = await instance.database; @@ -291,6 +307,30 @@ class LifeListProvider { return count; } + Future allBirdCount(String commonGroup) async { + final db = await instance.database; + final int count = Sqflite.firstIntValue( + await db.query('allBirds', + columns: ['COUNT(*)'], + where: 'commonGroup = ?', + whereArgs: [commonGroup]), + ) ?? + 0; + return count; + } + + Future lifeBirdCount(String commonGroup) async { + final db = await instance.database; + final int count = Sqflite.firstIntValue( + await db.query('birds', + columns: ['COUNT(*)'], + where: 'commonGroup = ?', + whereArgs: [commonGroup]), + ) ?? + 0; + return count; + } + Future containsProvData() async { final db = await instance.database; final int count = Sqflite.firstIntValue( diff --git a/beakpeek/lib/Model/UserProfile/achievment_list.dart b/beakpeek/lib/Model/UserProfile/achievment_list.dart index 21315ebe..23b2ace8 100644 --- a/beakpeek/lib/Model/UserProfile/achievment_list.dart +++ b/beakpeek/lib/Model/UserProfile/achievment_list.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:io'; +import 'package:beakpeek/Controller/DB/life_list_provider.dart'; import 'package:beakpeek/Model/UserProfile/user_achievment.dart'; import 'package:beakpeek/Model/UserProfile/user_model.dart'; import 'package:beakpeek/config_azure.dart'; @@ -97,3 +98,89 @@ AchievementList? getLocalAchievmentList() { } return AchievementList.fromJson(userString); } + +Future updateLifeListAchievments(String commonGroup, int id) async { + switch (commonGroup.toLowerCase()) { + case 'weaver': + updateAchievmentProgressBirds('Weaver Believer', 'weaver'); + updateProvinces(id); + break; + case 'duck': + updateAchievmentProgressBirds('Duck Hunter', 'duck'); + updateProvinces(id); + break; + case 'egale': + updateAchievmentProgressBirds('USA', 'egale'); + updateProvinces(id); + break; + case 'kingfisher': + updateAchievmentProgressBirds('Fisherman', 'kingfisher'); + updateProvinces(id); + break; + case 'hawk': + updateAchievmentProgressBirds('Hawk Spotter', 'hawk'); + updateProvinces(id); + break; + case 'heron': + updateAchievmentProgressBirds('Heron Horror', 'heron'); + + break; + } + updateProvinces(id); +} + +Future updateProvinces(int id) async { + final List achivementNames = [ + 'Risk it for the Biscuits', + 'Gauteng Explorer', + 'Watch The Waves', + 'Limpopo Hoopoe', + 'Mpumalanga Adventurer', + 'Dimond Hunter', + 'Directionally challanged', + 'Western Cape Wanderer', + 'Freesest Alive' + ]; + final LifeListProvider lifeList = LifeListProvider.instance; + final List progress = await lifeList.precentLifeListBirds(); + for (int i = 0; i < achivementNames.length; i++) { + final Achievement? provMaster = getAchievementByName(achivementNames[i]); + + if (provMaster != null) { + await updateUsersAchievement(provMaster.id, progress[i]); + //print('progress at $i ${progress[i]}'); + for (UserAchievement ua in user.achievements) { + print(ua.toJson()); + } + print(achivementNames[i]); + } + } +} + +/// All checks for user progress should go here +Future updateAchievmentProgressBirds( + String achievemntName, String commonGroup) async { + /// Quiz Master + final Achievement? birdMaster = getAchievementByName(achievemntName); + final LifeListProvider lifeList = LifeListProvider.instance; + if (birdMaster != null) { + final int countBird = await lifeList.lifeBirdCount(commonGroup); + final int totalBird = await lifeList.allBirdCount(commonGroup); + double progress = countBird / totalBird; + progress = progress < 1 ? progress : 1; + await updateUsersAchievement(birdMaster.id, progress); + for (UserAchievement ua in user.achievements) { + print(ua.toJson()); + } + print(achievemntName); + } + + for (UserAchievement userAchievement in user.achievements) { + achievementList + .achievements[achievementList.achievements + .indexWhere((element) => element.id == userAchievement.id)] + .progress = userAchievement.progress; + } + + storeAchievementListLocally(achievementList); +} diff --git a/beakpeek/lib/Model/UserProfile/user_model.dart b/beakpeek/lib/Model/UserProfile/user_model.dart index 5e2c55dd..919ff292 100644 --- a/beakpeek/lib/Model/UserProfile/user_model.dart +++ b/beakpeek/lib/Model/UserProfile/user_model.dart @@ -97,12 +97,12 @@ Future getOnlineUser() async { return user; } user = UserModel.fromJson(response.body); - - final String localLife = await lifelist.updateUserLifelist(); + final String localLife = await lifelist.fetchUserLifelistString(); if (localLife.length < user.lifelist.length) { - final Map valueMap = json.decode(localLife); - valueMap.forEach((key, value) { - lifelist.insertBird(value); + final List valueList = json.decode(user.lifelist); + valueList.forEach((element) { + final int birdId = element['id']; + lifelist.insertBird(birdId); }); } else { user.lifelist = localLife; @@ -134,9 +134,11 @@ void deleteLocalUser() { Future updateOnline( {LifeListProvider? lifelist, bool logout = false}) async { - // if (lifelist != null) { - // await lifelist.updateUserLifelist(); - // } + lifelist = LifeListProvider.instance; + if (lifelist != null) { + String list = await lifelist.updateUserLifelist(); + user.set('lifelist', list); + } final response = await http.post(Uri.parse('$userApiUrl/User/UpdateProfile'), headers: { HttpHeaders.authorizationHeader: 'Bearer $accessToken', @@ -144,6 +146,7 @@ Future updateOnline( }, body: user.toJson()); + // print(json.decode(response.body)); if (logout) { return; } diff --git a/beakpeek/lib/View/Sightings/sightings.dart b/beakpeek/lib/View/Sightings/sightings.dart index beefdabd..1041a2b7 100644 --- a/beakpeek/lib/View/Sightings/sightings.dart +++ b/beakpeek/lib/View/Sightings/sightings.dart @@ -38,8 +38,6 @@ class _SightingsState extends State { context.goNamed( 'birdInfo', pathParameters: { - 'group': bird.commonGroup, - 'species': bird.commonSpecies, 'id': bird.id.toString(), }, ); diff --git a/beakpeek/pubspec.yaml b/beakpeek/pubspec.yaml index 7e1f84bf..89d50d2b 100644 --- a/beakpeek/pubspec.yaml +++ b/beakpeek/pubspec.yaml @@ -118,10 +118,10 @@ flutter: - assets/province_limpopo.kml - assets/province_northerncape.kml - assets/province_northwest.kml - # - assets/Legal/combinepdf.pdf + - assets/Legal/combinepdf.pdf - assets/Legal/BeakPeekTermsOfUse.pdf - assets/Legal/BeakPeekPrivacyPolicy.pdf - - assets/Legal/BeakPeekCookiePolicy.pdf + - assets/Legal/BeakPeekCookiePolicy.pdf # - images/a_dot_ham.jpeg # An image asset can refer to one or more resolution-specific "variants", see diff --git a/dotnet/UserApi/UserApi/Views/AppUser/Index.cshtml b/dotnet/UserApi/UserApi/Views/AppUser/Index.cshtml index 0dbaf287..11b3b101 100644 --- a/dotnet/UserApi/UserApi/Views/AppUser/Index.cshtml +++ b/dotnet/UserApi/UserApi/Views/AppUser/Index.cshtml @@ -1,75 +1,100 @@ @model IEnumerable -@{ + @{ ViewData["Title"] = "Index"; -} + } -

Index

+

Index

-

- Create New -

- - - - - - - - - - - - - - - - - - - -@foreach (var item in Model) { - - - - - - -
- @Html.DisplayNameFor(model => model.ProfilePicture) - - @Html.DisplayNameFor(model => model.UserName) - - @Html.DisplayNameFor(model => model.NormalizedUserName) - - @Html.DisplayNameFor(model => model.Email) - - @Html.DisplayNameFor(model => model.NormalizedEmail) - - @Html.DisplayNameFor(model => model.EmailConfirmed) - - @Html.DisplayNameFor(model => model.PhoneNumber) - - @Html.DisplayNameFor(model => model.PhoneNumberConfirmed) - - @Html.DisplayNameFor(model => model.TwoFactorEnabled) - - @Html.DisplayNameFor(model => model.LockoutEnd) - - @Html.DisplayNameFor(model => model.LockoutEnabled) - - @Html.DisplayNameFor(model => model.AccessFailedCount) -
- @Html.DisplayFor(modelItem => item.ProfilePicture, "imageDisplay") - - @Html.DisplayFor(modelItem => item.UserName) - - @Html.DisplayFor(modelItem => item.NormalizedUserName) - - @Html.DisplayFor(modelItem => item.Email) - - @Html.DisplayFor(modelItem => item.NormalizedEmail) - +

+ Create New +

+ + + + + + + + + + + + + + + + + + @foreach (var item in Model) { + + + + + + + + + + + + + + + } + +
+ @Html.DisplayNameFor(model => model.ProfilePicture) + + @Html.DisplayNameFor(model => model.UserName) + + @Html.DisplayNameFor(model => model.Email) + + @Html.DisplayNameFor(model => model.XP) + + @Html.DisplayNameFor(model => model.Level) + + @Html.DisplayNameFor(model => model.Lifelist) + + @Html.DisplayNameFor(model => model.Highscore) +
+ @Html.DisplayFor(modelItem => item.ProfilePicture, "imageDisplay") + + @Html.DisplayFor(modelItem => item.UserName) + + @Html.DisplayFor(modelItem => item.Email) + + @Html.DisplayFor(modelItem => item.XP) + + @Html.DisplayFor(modelItem => item.Level) + + @Html.DisplayFor(modelItem => item.Lifelist) + + @Html.DisplayFor(modelItem => item.Highscore) + + @* Edit | *@ + Details | + Delete +
diff --git a/scripts/bash/remove_load_test_user.sh b/scripts/bash/remove_load_test_user.sh new file mode 100755 index 00000000..28e0356e --- /dev/null +++ b/scripts/bash/remove_load_test_user.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +for i in {1..1000}; do + response=$(curl --request POST \ + 'https://beakpeekuserapi.azurewebsites.net/User/Login' \ + --header 'Content-Type: application/json' \ + --data '{"email": "'"${i}"'@load","password": "Test@test1"}') + + token=$(echo $response ) + echo "found $token" + + if [ "$token" != "null" ] && [ -n "$token" ]; then + second_response=$(curl --request GET 'https://beakpeekuserapi.azurewebsites.net/User/Delete' --header "Authorization: Bearer ${token}") + echo "delete response: $second_response" + echo "Deleted user $i@load" + else + echo "No user found with $i@load" + fi +done