Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions src/Application/Interface/ISchoolService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ public interface ISchoolService
#region Job

Task<ResultData<bool>> UpdateSchoolScoreAsync();
Task<ResultData<bool>> UpdateSchoolCommentsRatingAsync();
Task<ResultData<bool>> UpdateSchoolCommentReactionsAsync(long? schoolCommentId = null);
Task<ResultData<bool>> RemoveOldRejectedSchoolImagesAsync();

Expand Down
80 changes: 59 additions & 21 deletions src/Application/Service/SchoolService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public async Task<ResultData<ListDataSource<SchoolsDto>>> GetSchoolsAsync(ListRe
t.CountryRank,
t.StateRank,
t.CityRank,
Rating = t.CommentsRatingSum != null ? t.CommentsRatingSum / t.CommentsRatingCount : null,
}).ToListAsync();
if (schools is null || schools.Count == 0)
{
Expand Down Expand Up @@ -106,6 +107,7 @@ public async Task<ResultData<ListDataSource<SchoolsDto>>> GetSchoolsAsync(ListRe
CountryRank = schools[i].CountryRank,
StateRank = schools[i].StateRank,
CityRank = schools[i].CityRank,
Rating = schools[i].Rating,
});
}

Expand Down Expand Up @@ -137,7 +139,7 @@ public async Task<ResultData<ListDataSource<SchoolInfoDto>>> GetSchoolsListAsync
t.Email,
t.PhoneNumber,
t.Coordinates,
t.RankScore,
Rating = t.CommentsRatingSum != null ? t.CommentsRatingSum / t.CommentsRatingCount : null,
t.CityId,
t.StateId,
t.CountryId,
Expand All @@ -146,7 +148,6 @@ public async Task<ResultData<ListDataSource<SchoolInfoDto>>> GetSchoolsListAsync
t.StateRank,
t.CityRank,
Distance = point != null && t.Coordinates != null ? t.Coordinates.Distance(point) : (double?)null,
Rating = t.SchoolComments.Any() ? t.SchoolComments.Average(c => c.AverageRate) : (double?)null,
});

(query, var sortApplied) = query.OrderBy(requestDto?.PagingDto?.SortFilter);
Expand Down Expand Up @@ -290,7 +291,7 @@ public async Task<ResultData<SchoolDto>> GetSchoolAsync([NotNull] ISpecification
t.CountryRank,
t.StateRank,
t.CityRank,
Rating = t.SchoolComments.Any() ? t.SchoolComments.Average(c => c.AverageRate) : (double?)null,
Rating = t.CommentsRatingSum != null ? t.CommentsRatingSum / t.CommentsRatingCount : null,
}).FirstOrDefaultAsync();
if (school is null)
{
Expand Down Expand Up @@ -851,6 +852,8 @@ private async Task CreateSchoolCommentAsync(SchoolCommentContributionDto dto)
{
var uow = UnitOfWorkProvider.Value.CreateUnitOfWork();
var schoolCommentRepository = uow.GetRepository<SchoolComment>();
var trn = uow.CreateTransactionScope();

schoolCommentRepository.Add(new()
{
SchoolId = dto.SchoolId,
Expand All @@ -868,6 +871,12 @@ private async Task CreateSchoolCommentAsync(SchoolCommentContributionDto dto)
CreationDate = dto.CreationDate,
});
_ = await uow.SaveChangesAsync();

_ = await uow.GetRepository<School>().GetManyQueryable(t => t.Id == dto.SchoolId).ExecuteUpdateAsync(t => t
.SetProperty(p => p.CommentsRatingSum, p => p.CommentsRatingSum + dto.AverageRate)
.SetProperty(p => p.CommentsRatingCount, p => p.CommentsRatingCount + 1));

trn.Complete();
}

public async Task<ResultData<bool>> ConfirmSchoolCommentContributionAsync([NotNull] ConfirmSchoolCommentContributionRequestDto requestDto)
Expand Down Expand Up @@ -1901,31 +1910,25 @@ public async Task<ResultData<bool>> UpdateSchoolScoreAsync()
{
var uow = UnitOfWorkProvider.Value.CreateUnitOfWork();
var query = $@"
;WITH CommentAgg AS
(
SELECT c.SchoolId, AVG(CONVERT(decimal(10,2), c.AverageRate)) * 10 AS CommentScore FROM SchoolComments c GROUP BY c.SchoolId
),
ImageAgg AS
;WITH ImageAgg AS
(
SELECT i.SchoolId, CASE WHEN COUNT_BIG(*) >= 5 THEN 50 ELSE COUNT_BIG(*) * 10 END AS ImageScore FROM SchoolImages i GROUP BY i.SchoolId
),
RankScoreCalc AS
ScoreCalc AS
(
SELECT
s.Id,
s.CountryId,
s.StateId,
s.CityId,
RankScore =
ISNULL(ca.CommentScore, 0)
Score = ISNULL(ia.ImageScore, 0)
+ CASE WHEN s.CommentsRatingSum IS NOT NULL THEN s.CommentsRatingSum/s.CommentsRatingCount ELSE 0 END
+ CASE WHEN s.Coordinates IS NOT NULL THEN 10 ELSE 0 END
+ CASE WHEN s.WebSite IS NOT NULL AND s.WebSite <> '' THEN 25 ELSE 0 END
+ CASE WHEN s.Email IS NOT NULL AND s.Email <> '' THEN 5 ELSE 0 END
+ CASE WHEN s.PhoneNumber IS NOT NULL AND s.PhoneNumber <> '' THEN 5 ELSE 0 END
+ CASE WHEN s.Address IS NOT NULL AND s.Address <> '' THEN 5 ELSE 0 END
+ ISNULL(ia.ImageScore, 0)
FROM Schools s
LEFT JOIN CommentAgg ca ON ca.SchoolId = s.Id
LEFT JOIN ImageAgg ia ON ia.SchoolId = s.Id
),
RankCalc AS
Expand All @@ -1934,30 +1937,27 @@ RankCalc AS
sc.*,
CASE
WHEN sc.CountryId IS NULL THEN NULL
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId ORDER BY sc.RankScore DESC)
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId ORDER BY sc.Score DESC)
END AS CountryRank,
CASE
WHEN sc.StateId IS NULL THEN NULL
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId, sc.StateId ORDER BY sc.RankScore DESC)
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId, sc.StateId ORDER BY sc.Score DESC)
END AS StateRank,
CASE
WHEN sc.CityId IS NULL THEN NULL
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId, sc.StateId, sc.CityId ORDER BY sc.RankScore DESC)
ELSE DENSE_RANK() OVER (PARTITION BY sc.CountryId, sc.StateId, sc.CityId ORDER BY sc.Score DESC)
END AS CityRank
FROM RankScoreCalc sc
FROM ScoreCalc sc
)
UPDATE s
SET
s.RankScore = rc.RankScore,
s.CountryRank = rc.CountryRank,
s.StateRank = rc.StateRank,
s.CityRank = rc.CityRank
FROM Schools s
JOIN RankCalc rc ON rc.Id = s.Id
WHERE
s.RankScore <> rc.RankScore
OR s.RankScore IS NULL
OR s.CountryRank <> rc.CountryRank
s.CountryRank <> rc.CountryRank
OR s.CountryRank IS NULL
OR s.StateRank <> rc.StateRank
OR s.StateRank IS NULL
Expand All @@ -1975,6 +1975,44 @@ OR s.CityRank <> rc.CityRank
}
}

public async Task<ResultData<bool>> UpdateSchoolCommentsRatingAsync()
{
try
{
var uow = UnitOfWorkProvider.Value.CreateUnitOfWork();
var query = $@"
;WITH CommentAgg AS
(
SELECT c.SchoolId, SUM(CONVERT(decimal(10,2), c.AverageRate)) AS RateSum , COUNT(1) AS RateCount
FROM SchoolComments c GROUP BY c.SchoolId
),
RankCal AS
(
SELECT
s.Id,
ca.RateSum,
ca.RateCount
FROM Schools s
LEFT JOIN CommentAgg ca ON ca.SchoolId = s.Id
)
UPDATE s
SET
s.CommentsRatingSum = rc.RateSum,
s.CommentsRatingCount = rc.RateCount
FROM Schools s
JOIN RankCal rc ON rc.Id = s.Id
";
_ = await uow.ExecuteSqlCommandAsync(query);

return new(OperationResult.Succeeded) { Data = true };
}
catch (Exception exc)
{
Logger.Value.LogException(exc);
return new(OperationResult.Failed) { Errors = [new() { Message = exc.Message, },] };
}
}

public async Task<ResultData<bool>> UpdateSchoolCommentReactionsAsync(long? schoolCommentId = null)
{
try
Expand Down
1 change: 1 addition & 0 deletions src/Core/Data/Dto/School/SchoolsDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public sealed class SchoolsDto
public int? CountryRank { get; set; }
public int? StateRank { get; set; }
public int? CityRank { get; set; }
public double? Rating { get; set; }
}
}
8 changes: 5 additions & 3 deletions src/Domain/Entity/School.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,11 @@ public class School : VersionableEntity<ApplicationUser, long, long?>, IEntity<S
[StringLength(300)]
public string? WebSite { get; set; }

[Column(nameof(RankScore), TypeName = "float")]
public double? RankScore { get; set; }
[Column(nameof(CommentsRatingSum), TypeName = "float")]
public double? CommentsRatingSum { get; set; }

[Column(nameof(CommentsRatingCount), DataType.Int)]
public int? CommentsRatingCount { get; set; }

[Column(nameof(IsDeleted), DataType.Boolean)]
public bool IsDeleted { get; set; }
Expand Down Expand Up @@ -127,7 +130,6 @@ public void Configure([NotNull] EntityTypeBuilder<School> builder)

_ = builder.HasQueryFilter(t => !t.IsDeleted).HasIndex(t => t.IsDeleted);

_ = builder.HasIndex(t => t.RankScore).IsDescending(true);
_ = builder.HasIndex(t => new { t.LastModifyDate, t.CreationDate }).IsDescending(true, true);

_ = builder.HasIndex(t => new { t.IsDeleted, t.Name });
Expand Down
2 changes: 1 addition & 1 deletion src/Domain/Specification/School/HasRatingSpecification.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace GamaEdtech.Domain.Specification.School

public sealed class HasRatingSpecification(bool hasRating) : SpecificationBase<School>
{
public override Expression<Func<School, bool>> Expression() => (t) => hasRating && t.SchoolComments.Any();
public override Expression<Func<School, bool>> Expression() => (t) => hasRating && t.CommentsRatingSum != null;
}
}
Loading