Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

data refresh update #37

Merged
merged 36 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
da1c116
Created statsservice to handle updating stats when collection or plan…
mariaruth1 May 28, 2024
7fe14d5
added service!
mariaruth1 May 28, 2024
58c63f0
updated test to reflect changes
mariaruth1 May 28, 2024
1f4cfde
split tests to avoid timeout issue in pipeline
mariaruth1 May 28, 2024
5ed77e4
avoid awaiting tasks individually to hopefulle speed up the testing p…
mariaruth1 May 28, 2024
c2edf04
further combined the requests to db
mariaruth1 May 28, 2024
c2c4e94
i dont event know anymore?
mariaruth1 May 28, 2024
53a6407
print received messages for debugging
juuwel May 28, 2024
15d472a
small extension to print out error messages
juuwel May 28, 2024
d598f22
Update readme.md
mariaruth1 May 28, 2024
927af06
Merge pull request #34 from Team-Wilhelm/mariaruth1-patch-1
mariaruth1 May 28, 2024
26b3af4
Update readme.md
mariaruth1 May 28, 2024
25ea313
Update readme.md
mariaruth1 May 28, 2024
80c9195
does it work?
juuwel May 28, 2024
a559127
ripped off websockettestclient to see if the timing is the problem
juuwel May 28, 2024
6b95429
can we log errors?
juuwel May 28, 2024
4fbad2d
listen for more errors
juuwel May 28, 2024
ebfac26
print error
juuwel May 28, 2024
d4b45c4
try without user info
juuwel May 28, 2024
6152c68
send detailed error in testing
juuwel May 28, 2024
5baa484
adjust pipeline not to require azure credentials as they are mocked e…
juuwel May 28, 2024
935ebb2
remove logging every message
juuwel May 28, 2024
8bd1d9b
Merge pull request #33 from Team-Wilhelm/final-tweaks
juuwel May 28, 2024
97e24fd
change populating database to a bit more realistic model
juuwel May 28, 2024
874146a
data now sent from server upon modifications from client instead of b…
mariaruth1 May 28, 2024
4359ef5
adjust creation of historic data
juuwel May 28, 2024
69fdce1
fill in default plant image url while testing
juuwel May 28, 2024
91b0064
fix get critical plants not returning only the latest condition log, …
juuwel May 28, 2024
91c04d0
added check for existing plant with device id to create and update plant
mariaruth1 May 28, 2024
e77ff8f
Merge pull request #35 from Team-Wilhelm/populating-real-data
mariaruth1 May 28, 2024
fe71f75
reshuffle some method calls
juuwel May 29, 2024
cabeb4e
send initial data on authentication
mariaruth1 May 29, 2024
109d62b
Merge remote-tracking branch 'refs/remotes/origin/main' into initial-…
mariaruth1 May 29, 2024
85441f2
Merge remote-tracking branch 'origin/initial-data-flow-reversal' into…
mariaruth1 May 29, 2024
b22d101
Merge pull request #36 from Team-Wilhelm/initial-data-flow-reversal
juuwel May 29, 2024
d821085
Merge branch 'deployed' into main
juuwel May 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,4 @@ jobs:
MQTT_CLIENT_ID: ${{ secrets.MQTT_CLIENT_ID }}
MQTT_SUBSCRIBE_TOPIC: ${{ secrets.MQTT_SUBSCRIBE_TOPIC }}
MQTT_PUBLISH_TOPIC: ${{ secrets.MQTT_PUBLISH_TOPIC }}

AZURE_VISION_KEY: ${{ secrets.AZURE_VISION_KEY }}
AZURE_VISION_ENDPOINT: ${{ secrets.AZURE_VISION_ENDPOINT }}
run: cd Tests && dotnet test
168 changes: 0 additions & 168 deletions Infrastructure/ApplicationDbContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,172 +54,4 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)

base.OnModelCreating(modelBuilder);
}

public async Task SeedDevelopmentDataAsync(IServiceScope scope, string defaultPlantImage)
{
var userRepository = scope.ServiceProvider.GetRequiredService<UserRepository>();
await userRepository.CreateUser(new RegisterUserDto
{
Email = "[email protected]",
Password = "password",
Username = "bob"
});

var collectionsRepository = scope.ServiceProvider.GetRequiredService<CollectionsRepository>();
var collection1 = await collectionsRepository.CreateCollection(
new Collection
{
CollectionId = Guid.NewGuid(),
Name = "Succulents",
UserEmail = "[email protected]",
}
);
var collection2 = await collectionsRepository.CreateCollection(
new Collection
{
CollectionId = Guid.NewGuid(),
Name = "Cacti",
UserEmail = "[email protected]",
}
);

var plantRepository = scope.ServiceProvider.GetRequiredService<PlantRepository>();
await plantRepository.CreatePlant(
new Plant
{
PlantId = Guid.NewGuid(),
Nickname = "Aloe Vera",
UserEmail = "[email protected]",
ImageUrl = defaultPlantImage,
CollectionId = collection1.CollectionId,
LatestChange = DateTime.UtcNow.Subtract(TimeSpan.FromDays(5))
}
);

await plantRepository.CreatePlant(
new Plant
{
PlantId = Guid.NewGuid(),
Nickname = "Prickly Pear",
UserEmail = "[email protected]",
ImageUrl = defaultPlantImage,
CollectionId = collection2.CollectionId,
LatestChange = DateTime.UtcNow.Subtract(TimeSpan.FromDays(7))
}
);

await plantRepository.CreatePlant(
new Plant
{
PlantId = Guid.NewGuid(),
Nickname = "Dying plant",
UserEmail = "[email protected]",
ImageUrl = defaultPlantImage,
CollectionId = collection2.CollectionId,
LatestChange = DateTime.UtcNow
}
);

var plants = await plantRepository.GetPlantsForUser("[email protected]", 1, 5);

var requirementsRepository = scope.ServiceProvider.GetRequiredService<RequirementsRepository>();
await requirementsRepository.CreateRequirements(
new Requirements
{
RequirementsId = Guid.NewGuid(),
PlantId = plants.First(p => p.Nickname == "Aloe Vera").PlantId,
LightLevel = RequirementLevel.Low,
SoilMoistureLevel = RequirementLevel.Medium,
HumidityLevel = RequirementLevel.High,
TemperatureLevel = 22,
}
);

await requirementsRepository.CreateRequirements(
new Requirements
{
RequirementsId = Guid.NewGuid(),
PlantId = plants.First(p => p.Nickname == "Prickly Pear").PlantId,
LightLevel = RequirementLevel.High,
SoilMoistureLevel = RequirementLevel.Low,
HumidityLevel = RequirementLevel.Low,
TemperatureLevel = 27,
}
);

await requirementsRepository.CreateRequirements(
new Requirements
{
RequirementsId = Guid.NewGuid(),
PlantId = plants.First(p => p.Nickname == "Dying plant").PlantId,
LightLevel = RequirementLevel.High,
SoilMoistureLevel = RequirementLevel.Low,
HumidityLevel = RequirementLevel.Medium,
TemperatureLevel = 24,
}
);

var conditionsLogRepository = scope.ServiceProvider.GetRequiredService<ConditionsLogsRepository>();

for (var i = 0; i < 100; i++)
{
await conditionsLogRepository.CreateConditionsLogAsync(
GetRandomConditionsLog(plants.First(p => p.Nickname == "Prickly Pear").PlantId, i * 6)
);
await conditionsLogRepository.CreateConditionsLogAsync(
GetRandomConditionsLog(plants.First(p => p.Nickname == "Aloe Vera").PlantId, i * 6)
);
}

await conditionsLogRepository.CreateConditionsLogAsync(
new ConditionsLog()
{
ConditionsId = Guid.NewGuid(),
PlantId = plants.First(p => p.Nickname == "Dying plant").PlantId,
TimeStamp = DateTime.UtcNow,
Mood = 0,
SoilMoisture = 55,
Light = 13,
Humidity = 68,
Temperature = 25,
}
);

Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Seeded development data");
Console.ResetColor();
}

private double GetRandomLevelValue()
{
var random = new Random();
return random.NextDouble() * 100;
}

private int GetRandomMood()
{
var random = new Random();
return random.Next(0, 5);
}

private int GetRandomTemperature()
{
var random = new Random();
return random.Next(-20, 45);
}

private ConditionsLog GetRandomConditionsLog(Guid plantId, int hoursAgo = 0)
{
return new ConditionsLog
{
ConditionsId = Guid.NewGuid(),
PlantId = plantId,
TimeStamp = DateTime.UtcNow.Subtract(TimeSpan.FromHours(hoursAgo)),
Mood = GetRandomMood(),
SoilMoisture = GetRandomLevelValue(),
Light = GetRandomLevelValue(),
Temperature = GetRandomTemperature(),
Humidity = GetRandomLevelValue(),
};
}
}
7 changes: 0 additions & 7 deletions Infrastructure/Repositories/CollectionsRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,4 @@ public async Task RemovePlantFromCollection(Collection collection, Plant plant)
collection.Plants.Remove(plant);
await applicationDbContext.SaveChangesAsync();
}

public async Task<int> GetTotalCollectionsCount(string email)
{
await using var applicationDbContext = await dbContextFactory.CreateDbContextAsync();
return await applicationDbContext.Collections
.CountAsync(collection => collection.UserEmail == email);
}
}
29 changes: 19 additions & 10 deletions Infrastructure/Repositories/PlantRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,16 @@
public async Task CreatePlant(Plant plant)
{
await using var context = await dbContextFactory.CreateDbContextAsync();

await context.Plants.AddAsync(plant);
await context.SaveChangesAsync();
}

public async Task<bool> DoesDeviceIdExist(string deviceId)
{
await using var context = await dbContextFactory.CreateDbContextAsync();
return await context.Plants.AnyAsync(p => p.DeviceId == deviceId);
}

public async Task<Plant?> GetPlantById(Guid id)
{
Expand Down Expand Up @@ -98,7 +105,7 @@
await using var context = await dbContextFactory.CreateDbContextAsync();
return await context.Plants
.Include(plant => plant.Requirements)
.Include(plant => plant.ConditionsLogs)
.Include(plant => plant.ConditionsLogs.OrderByDescending(log => log.TimeStamp).Take(1))
.Where(p => p.UserEmail == requesterEmail && p.ConditionsLogs.Count != 0)
.Select(p => new
{
Expand All @@ -108,26 +115,28 @@
.ThenByDescending(log => log.TimeStamp)
.FirstOrDefault()
})
.OrderBy(p => p.WorstMood.Mood)

Check warning on line 118 in Infrastructure/Repositories/PlantRepository.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
.ThenByDescending(p => p.WorstMood.TimeStamp)

Check warning on line 119 in Infrastructure/Repositories/PlantRepository.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
.Take(3)
.Select(p => p.Plant)
.ToListAsync();
}

public async Task<int> GetHappyPlantsCount(string userEmail)
public async Task<Stats> GetStats(string userEmail)
{
await using var context = await dbContextFactory.CreateDbContextAsync();
return await context.Plants
var totalPlants = await context.Plants.CountAsync(p => p.UserEmail == userEmail);
var happyPlants = await context.Plants
.Include(plant => plant.ConditionsLogs)
.Where(p => p.UserEmail == userEmail && p.ConditionsLogs.Count != 0)
.CountAsync(p => p.ConditionsLogs.OrderByDescending(log => log.TimeStamp).FirstOrDefault()!.Mood > 2);
}
var collections = await context.Collections.CountAsync(c => c.UserEmail == userEmail);

public async Task<int> GetTotalPlantsCount(string userEmail)
{
await using var context = await dbContextFactory.CreateDbContextAsync();
return await context.Plants
.CountAsync(p => p.UserEmail == userEmail);
return new Stats
{
TotalPlants = totalPlants,
HappyPlants = happyPlants,
Collections = collections
};
}
}
2 changes: 1 addition & 1 deletion Shared/Dtos/CreateConditionsLogDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ public class CreateConditionsLogDto
public double Light { get; set; }
public double Temperature { get; set; }
public double Humidity { get; set; }
public long DeviceId { get; set; }
public required string DeviceId { get; set; }
}
8 changes: 8 additions & 0 deletions Shared/Models/Stats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Shared.Models;

public class Stats
{
public int TotalPlants { get; set; }
public int HappyPlants { get; set; }
public int Collections { get; set; }
}
17 changes: 12 additions & 5 deletions Tests/PlantTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using api.Events.Collections.Server;
using api.Events.PlantEvents.Client;
using api.Events.PlantEvents.Server;
using lib;
Expand All @@ -15,16 +14,24 @@ public async Task CreatePlant()
{
var jwtAndEmail = await SignUpAndLogIn();
var jwt = jwtAndEmail[DictionaryKeys.Jwt];
var email = jwtAndEmail[DictionaryKeys.Email];
var createPlantDto = GenerateRandomCreatePlantDto(email);
var createPlantDto = GenerateRandomCreatePlantDto();

var webSocketTestClient = await new WebSocketTestClient().ConnectAsync();

await webSocketTestClient.DoAndAssert(new ClientWantsToCreatePlantDto { CreatePlantDto = createPlantDto, Jwt = jwt }, receivedMessages =>
{
return receivedMessages.Count(e => e.eventType == nameof(ServerSavesPlant)) == 1;
});

}

[Test]
public async Task GetAllPlants()
{
var jwtAndEmail = await SignUpAndLogIn();
var jwt = jwtAndEmail[DictionaryKeys.Jwt];

var webSocketTestClient = await new WebSocketTestClient().ConnectAsync();

await webSocketTestClient.DoAndAssert(new ClientWantsAllPlantsDto
{
Jwt = jwt,
Expand All @@ -36,7 +43,7 @@ await webSocketTestClient.DoAndAssert(new ClientWantsAllPlantsDto
});
}

private CreatePlantDto GenerateRandomCreatePlantDto(string email)
private CreatePlantDto GenerateRandomCreatePlantDto()
{
var createPlantDto = new CreatePlantDto
{
Expand Down
4 changes: 3 additions & 1 deletion Tests/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ await webSocketTestClient.DoAndAssert(new ClientWantsToSignUpDto { RegisterUserD

var jwtSubscription = webSocketTestClient.Client.MessageReceived.Subscribe(msg =>
{
var eventType = JsonSerializer.Deserialize<BaseDto>(msg.Text).eventType;
if (eventType != nameof(ServerAuthenticatesUser)) return;
var serverAuthenticates = JsonSerializer.Deserialize<ServerAuthenticatesUser>(msg.Text, options: new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
jwt = serverAuthenticates.Jwt;
jwt = serverAuthenticates!.Jwt;
});
webSocketTestClient.Send(new ClientWantsToLogInDto { LoginDto = loginDto });

Expand Down
69 changes: 69 additions & 0 deletions Tests/WebSocketTestClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Text.Json;
using api.Events.Global;
using lib;
using Websocket.Client;

namespace Tests
{
public class WebSocketTestClient
{
public readonly WebsocketClient Client;
public readonly List<BaseDto> ReceivedMessages = [];
private static readonly JsonSerializerOptions JsonSerializerOptions = new()
{
PropertyNameCaseInsensitive = true
};

public WebSocketTestClient(string? url = null)
{
Client = url == null ? new WebsocketClient(new Uri("ws://localhost:" + (Environment.GetEnvironmentVariable("FULLSTACK_API_PORT") ?? "8181"))) : new WebsocketClient(new Uri(url));
Client.MessageReceived.Subscribe(msg =>
{
BaseDto baseDto = JsonSerializer.Deserialize<BaseDto>(msg.Text, JsonSerializerOptions);

if (baseDto.eventType == "ServerSendsErrorMessage" || baseDto.eventType.Contains("ServerResponds") ||
baseDto.eventType.Contains("ServerRejects"))
{
var error = JsonSerializer.Deserialize<ServerSendsErrorMessage>(msg.Text, JsonSerializerOptions);
Console.WriteLine("Error: " + error!.Error);
}

lock (ReceivedMessages)
ReceivedMessages.Add(baseDto);
});
}

public async Task<WebSocketTestClient> ConnectAsync()
{
await Client.Start();
if (!Client.IsRunning)
throw new Exception("Could not start client!");
return this;
}

public void Send<T>(T dto) where T : BaseDto
{
Client.Send(JsonSerializer.Serialize(dto));
}

public async Task DoAndAssert<T>(T? action = null, Func<List<BaseDto>, bool>? condition = null) where T : BaseDto
{
if ((object) (T) action != null)
Send(action);
if (condition != null)
{
DateTime startTime = DateTime.UtcNow;
while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(5.0))
{
lock (ReceivedMessages)
{
if (condition(ReceivedMessages))
return;
}
await Task.Delay(100);
}
throw new TimeoutException("Condition not met: ");
}
}
}
}
Loading
Loading