count courses by school
This commit is contained in:
@ -15,6 +15,7 @@ var (
|
||||
dbStatementAttr = attribute.Key("db.statement")
|
||||
dbOperationAttr = attribute.Key("db.operation")
|
||||
dbTableAttr = attribute.Key("db.sql.table")
|
||||
dbArgumentsAttr = attribute.Key("db.arguments")
|
||||
)
|
||||
|
||||
type domainer[T any] interface {
|
||||
|
||||
@ -11,10 +11,12 @@ import (
|
||||
|
||||
"git.loyso.art/frx/kurious/internal/common/nullable"
|
||||
"git.loyso.art/frx/kurious/internal/common/xcontext"
|
||||
"git.loyso.art/frx/kurious/internal/common/xslices"
|
||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
)
|
||||
|
||||
@ -188,6 +190,66 @@ func (r *sqliteCourseRepository) ListCourseThematics(
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *sqliteCourseRepository) ListStatistics(
|
||||
ctx context.Context,
|
||||
params domain.ListStatisticsParams,
|
||||
) (result domain.ListStatisticsResult, err error) {
|
||||
const queryTemplate = `SELECT learning_type, course_thematic, organization_id, count(id) as count` +
|
||||
` FROM courses` +
|
||||
` WHERE 1=1`
|
||||
|
||||
query := queryTemplate
|
||||
args := make([]any, 0, 3)
|
||||
|
||||
if params.LearningTypeID != "" {
|
||||
query += ` AND learning_type = ?`
|
||||
args = append(args, params.LearningTypeID)
|
||||
}
|
||||
if params.CourseThematicID != "" {
|
||||
query += ` AND course_thematic = ?`
|
||||
args = append(args, params.CourseThematicID)
|
||||
}
|
||||
if params.OrganizaitonID != "" {
|
||||
query += ` AND organization_id = ?`
|
||||
args = append(args, params.OrganizaitonID)
|
||||
}
|
||||
|
||||
query += ` GROUP BY learning_type, course_thematic, organization_id`
|
||||
query += ` ORDER BY count(id) DESC`
|
||||
|
||||
ctx, span := dbTracer.Start(
|
||||
ctx, "list courses.statistics",
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(
|
||||
r.mergeAttributes(
|
||||
dbOperationAttr.String("SELECT"),
|
||||
dbStatementAttr.String(query),
|
||||
dbArgumentsAttr.StringSlice(argumentsAsStrings(args...)),
|
||||
)...,
|
||||
),
|
||||
)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
span.RecordError(err)
|
||||
span.SetStatus(codes.Error, err.Error())
|
||||
} else {
|
||||
span.SetStatus(codes.Ok, "finished")
|
||||
}
|
||||
|
||||
span.End()
|
||||
}()
|
||||
|
||||
var stats []sqliteCourseStatistic
|
||||
err = r.db.SelectContext(ctx, &stats, query, args...)
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("executing query: %w", err)
|
||||
}
|
||||
|
||||
result.LearningTypeStatistics = xslices.Map(stats, asDomainFunc)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (r *sqliteCourseRepository) Get(
|
||||
ctx context.Context,
|
||||
id string,
|
||||
@ -395,6 +457,17 @@ type sqliteCourseDB struct {
|
||||
DeletedAt sql.NullTime `db:"deleted_at"`
|
||||
}
|
||||
|
||||
type sqliteCourseStatistic struct {
|
||||
LearningTypeID string `db:"learning_type"`
|
||||
CourseThematicID string `db:"course_thematic"`
|
||||
OrganizationID string `db:"organization_id"`
|
||||
Count int `db:"count"`
|
||||
}
|
||||
|
||||
func (s sqliteCourseStatistic) AsDomain() domain.StatisticUnit {
|
||||
return domain.StatisticUnit(s)
|
||||
}
|
||||
|
||||
func nullStringAsDomain(s sql.NullString) nullable.Value[string] {
|
||||
if s.Valid {
|
||||
return nullable.NewValue(s.String)
|
||||
@ -482,3 +555,11 @@ func (c *sqliteCourseRepository) mergeAttributes(custom ...attribute.KeyValue) [
|
||||
|
||||
return append(outbase, custom...)
|
||||
}
|
||||
|
||||
func argumentsAsStrings(args ...any) []string {
|
||||
out := make([]string, 0, len(args))
|
||||
for _, arg := range args {
|
||||
out = append(out, fmt.Sprintf("%v", arg))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@ -106,12 +106,28 @@ func (r *sqliteOrganizationRepository) ListStats(
|
||||
ctx context.Context,
|
||||
params domain.ListOrganizationsParams,
|
||||
) (out []domain.OrganizationStat, err error) {
|
||||
const query = `SELECT o.id as id, o.external_id as external_id, o.name as name, COUNT(c.id) as courses_count` +
|
||||
` FROM organizations o` +
|
||||
` INNER JOIN courses c ON o.id = c.organization_id` +
|
||||
` GROUP BY o.id, o.external_id, o.name` +
|
||||
` ORDER BY COUNT(c.id) DESC`
|
||||
const queryTemplate = `WITH cte as (
|
||||
SELECT learning_type, course_thematic, organization_id, count(id) as courses_count
|
||||
FROM courses
|
||||
WHERE 1=1 {whereSuffix}
|
||||
GROUP BY learning_type, course_thematic, organization_id
|
||||
) SELECT o.id as id, o.external_id as external_id, o.name as name, cte.courses_count as courses_count
|
||||
FROM cte
|
||||
INNER JOIN organizations o ON o.id = cte.organization_id`
|
||||
|
||||
whereSuffix := ""
|
||||
|
||||
args := make([]any, 0, 3)
|
||||
if params.LearningTypeID != "" {
|
||||
whereSuffix += " AND learning_type = ?"
|
||||
args = append(args, params.LearningTypeID)
|
||||
}
|
||||
if params.CourseThematicID != "" {
|
||||
whereSuffix += " AND course_thematic = ?"
|
||||
args = append(args, params.CourseThematicID)
|
||||
}
|
||||
|
||||
query := strings.Replace(queryTemplate, "{whereSuffix}", whereSuffix, 1)
|
||||
ctx, span := dbTracer.Start(
|
||||
ctx, "list_stats courses",
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
@ -130,7 +146,7 @@ func (r *sqliteOrganizationRepository) ListStats(
|
||||
}()
|
||||
|
||||
var stats []organizationStatDB
|
||||
err = r.db.SelectContext(ctx, &stats, query)
|
||||
err = r.db.SelectContext(ctx, &stats, query, args...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("executing query: %w", err)
|
||||
}
|
||||
|
||||
@ -2,26 +2,35 @@ package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"git.loyso.art/frx/kurious/internal/common/decorator"
|
||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||
)
|
||||
|
||||
type ListCoursesStats struct{}
|
||||
type ListCoursesStats struct {
|
||||
LearningTypeID string
|
||||
CourseThematicsID string
|
||||
OrganizationID string
|
||||
}
|
||||
|
||||
type ListCoursesStatsResult struct{}
|
||||
|
||||
type ListCoursesStatsHandler decorator.QueryHandler[ListCoursesStats, domain.ListCoursesStatsResult]
|
||||
|
||||
type listCoursesStatsHandler struct {
|
||||
repo domain.CourseRepository
|
||||
mapper domain.CourseMapper
|
||||
}
|
||||
|
||||
func NewListCoursesStatsHandler(
|
||||
mapper domain.CourseMapper,
|
||||
repo domain.CourseRepository,
|
||||
log *slog.Logger,
|
||||
) ListCoursesStatsHandler {
|
||||
h := listCoursesStatsHandler{
|
||||
repo: repo,
|
||||
mapper: mapper,
|
||||
}
|
||||
|
||||
@ -32,7 +41,29 @@ func (h listCoursesStatsHandler) Handle(
|
||||
ctx context.Context,
|
||||
query ListCoursesStats,
|
||||
) (out domain.ListCoursesStatsResult, err error) {
|
||||
stats := h.mapper.GetStats(false)
|
||||
out.StatsByLearningType = stats
|
||||
if query.OrganizationID != "" {
|
||||
statistics, err := h.repo.ListStatistics(ctx, domain.ListStatisticsParams{
|
||||
LearningTypeID: query.LearningTypeID,
|
||||
CourseThematicID: query.CourseThematicsID,
|
||||
OrganizaitonID: query.OrganizationID,
|
||||
})
|
||||
if err != nil {
|
||||
return out, fmt.Errorf("listing statistics: %w", err)
|
||||
}
|
||||
|
||||
out.StatsByLearningType = make(map[string]domain.LearningTypeStat, len(statistics.LearningTypeStatistics))
|
||||
for _, unit := range statistics.LearningTypeStatistics {
|
||||
stats, ok := out.StatsByLearningType[unit.LearningTypeID]
|
||||
stats.Count++
|
||||
if !ok {
|
||||
stats.CourseThematic = make(map[string]int)
|
||||
}
|
||||
stats.CourseThematic[unit.CourseThematicID]++
|
||||
out.StatsByLearningType[unit.LearningTypeID] = stats
|
||||
}
|
||||
} else {
|
||||
stats := h.mapper.GetStats(false)
|
||||
out.StatsByLearningType = stats
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
@ -10,7 +10,9 @@ import (
|
||||
)
|
||||
|
||||
type ListOrganizationsStats struct {
|
||||
IDs []string
|
||||
LearningTypeID string
|
||||
CourseThematicID string
|
||||
IDs []string
|
||||
}
|
||||
|
||||
type ListOrganizationsStatsHandler decorator.QueryHandler[
|
||||
@ -38,7 +40,9 @@ func (h listOrganizationsStatsHandler) Handle(
|
||||
query ListOrganizationsStats,
|
||||
) ([]domain.OrganizationStat, error) {
|
||||
stats, err := h.repo.ListStats(ctx, domain.ListOrganizationsParams{
|
||||
IDs: query.IDs,
|
||||
LearningTypeID: query.LearningTypeID,
|
||||
CourseThematicID: query.CourseThematicID,
|
||||
IDs: query.IDs,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("listing stats: %w", err)
|
||||
|
||||
@ -18,6 +18,12 @@ type ListCoursesParams struct {
|
||||
Ascending bool
|
||||
}
|
||||
|
||||
type ListStatisticsParams struct {
|
||||
LearningTypeID string
|
||||
CourseThematicID string
|
||||
OrganizaitonID string
|
||||
}
|
||||
|
||||
type CreateCourseParams struct {
|
||||
ID string
|
||||
ExternalID nullable.Value[string]
|
||||
@ -63,12 +69,24 @@ type ListCourseThematicsResult struct {
|
||||
CourseThematicIDs []string
|
||||
}
|
||||
|
||||
type StatisticUnit struct {
|
||||
LearningTypeID string
|
||||
CourseThematicID string
|
||||
OrganizationID string
|
||||
Count int
|
||||
}
|
||||
|
||||
type ListStatisticsResult struct {
|
||||
LearningTypeStatistics []StatisticUnit
|
||||
}
|
||||
|
||||
//go:generate mockery --name CourseRepository
|
||||
type CourseRepository interface {
|
||||
// List courses by specifid parameters.
|
||||
List(context.Context, ListCoursesParams) (ListCoursesResult, error)
|
||||
ListLearningTypes(context.Context) (ListLearningTypeResult, error)
|
||||
ListCourseThematics(context.Context, ListCourseThematicsParams) (ListCourseThematicsResult, error)
|
||||
ListStatistics(context.Context, ListStatisticsParams) (ListStatisticsResult, error)
|
||||
// Get course by id.
|
||||
// Should return ErrNotFound in case course not found.
|
||||
Get(ctx context.Context, id string) (Course, error)
|
||||
@ -103,7 +121,9 @@ type CreateOrganizationParams struct {
|
||||
}
|
||||
|
||||
type ListOrganizationsParams struct {
|
||||
IDs []string
|
||||
LearningTypeID string
|
||||
CourseThematicID string
|
||||
IDs []string
|
||||
}
|
||||
|
||||
//go:generate mockery --name OrganizationRepository
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
package bootstrap
|
||||
|
||||
templ listCoursesByCourseThematic(params ListCoursesParams) {
|
||||
<div class="container">
|
||||
<h2>Здесь вы можете найти интересующие вас курсы
|
||||
по теме { params.FilterForm.ActiveLearningType.Name }:
|
||||
</h2>
|
||||
|
||||
<ul class="list-group">
|
||||
for _, courseThematic := range params.FilterForm.AvailableCourseThematics {
|
||||
<li class="list-group-item">
|
||||
<a href={templ.SafeURL("/courses/" + params.FilterForm.ActiveLearningType.ID + "/" + courseThematic.ID)}>
|
||||
{courseThematic.Name}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
|
||||
templ ListCourseThematics(pageType PageKind, s stats, params ListCoursesParams) {
|
||||
@root(pageType, s) {
|
||||
@listCoursesSectionHeader(params.FilterForm.BreadcrumbsParams)
|
||||
@listCoursesSectionFilters(params.FilterForm)
|
||||
@listCoursesByCourseThematic(params)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,154 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.513
|
||||
package bootstrap
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func listCoursesByCourseThematic(params ListCoursesParams) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"container\"><h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var2 := `Здесь вы можете найти интересующие вас курсы`
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var3 := `по теме `
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(params.FilterForm.ActiveLearningType.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list_course_thematics.templ`, Line: 5, Col: 59}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Var5 := `:`
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var5)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h2><ul class=\"list-group\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, courseThematic := range params.FilterForm.AvailableCourseThematics {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<li class=\"list-group-item\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = templ.SafeURL("/courses/" + params.FilterForm.ActiveLearningType.ID + "/" + courseThematic.ID)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 string
|
||||
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(courseThematic.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list_course_thematics.templ`, Line: 12, Col: 24}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</ul></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
|
||||
func ListCourseThematics(pageType PageKind, s stats, params ListCoursesParams) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var8 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var8 == nil {
|
||||
templ_7745c5c3_Var8 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var9 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
templ_7745c5c3_Err = listCoursesSectionHeader(params.FilterForm.BreadcrumbsParams).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = listCoursesSectionFilters(params.FilterForm).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = listCoursesByCourseThematic(params).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = root(pageType, s).Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
||||
@ -133,7 +133,11 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
statsresult, err := c.app.Queries.ListCourseStatistics.Handle(ctx, query.ListCoursesStats{})
|
||||
statsresult, err := c.app.Queries.ListCourseStatistics.Handle(ctx, query.ListCoursesStats{
|
||||
LearningTypeID: pathParams.LearningType,
|
||||
CourseThematicsID: pathParams.CourseThematic,
|
||||
OrganizationID: pathParams.School,
|
||||
})
|
||||
if handleError(ctx, err, w, c.log, "unable to load stats") {
|
||||
return
|
||||
}
|
||||
@ -178,24 +182,29 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
organizaions, err := c.app.Queries.ListOrganizationsStats.Handle(ctx, query.ListOrganizationsStats{})
|
||||
organizaions, err := c.app.Queries.ListOrganizationsStats.Handle(ctx, query.ListOrganizationsStats{
|
||||
LearningTypeID: pathParams.LearningType,
|
||||
CourseThematicID: pathParams.CourseThematic,
|
||||
})
|
||||
if handleError(ctx, err, w, c.log, "unable to list organizations") {
|
||||
return
|
||||
}
|
||||
|
||||
slices.SortFunc(organizaions, func(a, b domain.OrganizationStat) int {
|
||||
if a.CoursesCount > b.CoursesCount {
|
||||
organizationStatSortFunc := func(lhs, rhs domain.OrganizationStat) int {
|
||||
if lhs.CoursesCount > rhs.CoursesCount {
|
||||
return -1
|
||||
} else if a.CoursesCount < b.CoursesCount {
|
||||
} else if lhs.CoursesCount < rhs.CoursesCount {
|
||||
return 1
|
||||
}
|
||||
|
||||
if a.ID > b.ID {
|
||||
if lhs.ID > rhs.ID {
|
||||
return 1
|
||||
}
|
||||
|
||||
return -1
|
||||
})
|
||||
}
|
||||
|
||||
slices.SortFunc(organizaions, organizationStatSortFunc)
|
||||
|
||||
schools := xslices.Map(organizaions, func(in domain.OrganizationStat) bootstrap.NameIDPair {
|
||||
return bootstrap.NameIDPair{
|
||||
@ -250,7 +259,12 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
|
||||
span.AddEvent("starting to render")
|
||||
err = bootstrap.ListCourses(bootstrap.PageCourses, stats, params).Render(ctx, w)
|
||||
if pathParams.CourseThematic == "" {
|
||||
params.FilterForm.Render = true
|
||||
err = bootstrap.ListCourseThematics(bootstrap.PageCourses, stats, params).Render(ctx, w)
|
||||
} else {
|
||||
err = bootstrap.ListCourses(bootstrap.PageCourses, stats, params).Render(ctx, w)
|
||||
}
|
||||
span.AddEvent("render finished")
|
||||
|
||||
if handleError(ctx, err, w, c.log, "unable to render list courses") {
|
||||
|
||||
@ -55,14 +55,7 @@ func NewApplication(ctx context.Context, cfg ApplicationConfig, mapper domain.Co
|
||||
organizationrepo = sqliteConnection.Organization()
|
||||
repoCloser = sqliteConnection
|
||||
case RepositoryEngineYDB:
|
||||
ydbConnection, err := adapters.NewYDBConnection(ctx, cfg.YDB, log.With(slog.String("db", "ydb")))
|
||||
if err != nil {
|
||||
return Application{}, fmt.Errorf("making ydb connection: %w", err)
|
||||
}
|
||||
|
||||
courseadapter = ydbConnection.CourseRepository()
|
||||
organizationrepo = ydbConnection.Organization()
|
||||
repoCloser = ydbConnection
|
||||
return Application{}, errors.New("ydb is no longer supported")
|
||||
default:
|
||||
return Application{}, errors.New("unable to decide which db engine to use")
|
||||
}
|
||||
@ -85,7 +78,7 @@ func NewApplication(ctx context.Context, cfg ApplicationConfig, mapper domain.Co
|
||||
ListCourses: query.NewListCourseHandler(courseadapter, mapper, log),
|
||||
ListLearningTypes: query.NewListLearningTypesHandler(courseadapter, mapper, log),
|
||||
ListCourseThematics: query.NewListCourseThematicsHandler(courseadapter, mapper, log),
|
||||
ListCourseStatistics: query.NewListCoursesStatsHandler(mapper, log),
|
||||
ListCourseStatistics: query.NewListCoursesStatsHandler(mapper, courseadapter, log),
|
||||
GetCourse: query.NewGetCourseHandler(courseadapter, mapper, log),
|
||||
|
||||
ListOrganzations: query.NewListOrganizationsHandler(organizationrepo, log),
|
||||
|
||||
Reference in New Issue
Block a user