• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / backend / df5ec964a58b4388c6eb49286a6bd340a8a3837d

06 May 2026 03:24PM UTC coverage: 87.546% (-0.9%) from 88.458%
df5ec964a58b4388c6eb49286a6bd340a8a3837d

push

github

web-flow
Implemented Pixel Chatbot with 2 step RAG retrieval (#1349)

* write code for schema migration

* public louis code

* public louis code

* guest: fix rate limiter and 429 body

* pixel bot 2 step prompt and retrieval

* pull req writeup

* populate json map

* add context

* added admin panel

* admin panel louis

* feedback form

* remove guest

* removed extra files

* fixed sentry error

* fixed sentry bug

* ran format

* prevent empty prompt error

* ran mix format

* fixed credo issues

* fixed mix credo error

* fix credo changes

* added test cases

* fixed credo error

* added more test coverage

* added more test coverage

* Restore Elixir version

* Redate migration files to maintain total ordering

* Add more tests as suggested by AI

* Fix format

* Fix operator precedence

136 of 211 new or added lines in 11 files covered. (64.45%)

1 existing line in 1 file now uncovered.

4007 of 4577 relevant lines covered (87.55%)

6998.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.3
/lib/cadet_web/admin_controllers/admin_courses_controller.ex
1
defmodule CadetWeb.AdminCoursesController do
2
  use CadetWeb, :controller
3

4
  use PhoenixSwagger
5

6
  alias Cadet.Courses
7
  alias Cadet.Chatbot.CourseDocuments
8

9
  def update_course_config(conn, params = %{"course_id" => course_id})
10
      when is_ecto_id(course_id) do
11
    params = params |> to_snake_case_atom_keys()
5✔
12

13
    case Courses.update_course_config(course_id, params) do
5✔
14
      {:ok, _} ->
15
        text(conn, "OK")
3✔
16

17
      # coveralls-ignore-start
18
      # case of invalid course_id will not happen here
19
      {:error, {status, message}} ->
20
        send_resp(conn, status, message)
21

22
      # coveralls-ignore-stop
23

24
      {:error, _} ->
25
        conn
26
        |> put_status(:bad_request)
27
        |> text("Invalid parameter(s)")
2✔
28
    end
29
  end
30

31
  def get_assessment_configs(conn, %{"course_id" => course_id}) when is_ecto_id(course_id) do
32
    assessment_configs = Courses.get_assessment_configs(course_id)
1✔
33
    render(conn, "assessment_configs.json", %{configs: assessment_configs})
1✔
34
  end
35

36
  def update_assessment_configs(conn, %{
37
        "course_id" => course_id,
38
        "assessmentConfigs" => assessment_configs
39
      })
40
      when is_ecto_id(course_id) and is_list(assessment_configs) do
41
    if Enum.all?(assessment_configs, &is_map/1) do
3✔
42
      # coveralls-ignore-start
43
      configs =
44
        assessment_configs
45
        |> Enum.map(&to_snake_case_atom_keys/1)
46
        |> update_in(
47
          [Access.all()],
48
          &with(
49
            {v, m} <- Map.pop(&1, :display_in_dashboard),
50
            do: Map.put(m, :show_grading_summary, v)
51
          )
52
        )
53

54
      # coveralls-ignore-stop
55

56
      case Courses.mass_upsert_and_reorder_assessment_configs(course_id, configs) do
2✔
57
        {:ok, _} ->
58
          text(conn, "OK")
1✔
59

60
        {:error, {status, message}} ->
61
          conn
62
          |> put_status(status)
63
          |> text(message)
1✔
64
      end
65
    else
66
      send_resp(
1✔
67
        conn,
68
        :bad_request,
69
        "assessmentConfigs should be a list of assessment configuration objects"
70
      )
71
    end
72
  end
73

74
  def update_assessment_configs(conn, _) do
75
    send_resp(conn, :bad_request, "missing assessmentConfig")
2✔
76
  end
77

78
  def delete_assessment_config(conn, %{
79
        "course_id" => course_id,
80
        "assessment_config_id" => assessment_config_id
81
      })
82
      when is_ecto_id(course_id) and is_ecto_id(assessment_config_id) do
83
    case Courses.delete_assessment_config(course_id, assessment_config_id) do
2✔
84
      {:ok, _} ->
85
        text(conn, "OK")
1✔
86

87
      {:error, message} ->
88
        conn
89
        |> put_status(:bad_request)
90
        |> text(message)
1✔
91
    end
92
  end
93

94
  def get_document_map(conn, _params) do
NEW
95
    document_map = CourseDocuments.build_document_map_json()
×
NEW
96
    json(conn, %{documentMap: document_map})
×
97
  end
98

99
  swagger_path :update_course_config do
1✔
100
    put("/courses/{course_id}/admin/config")
101

102
    summary("Updates the course configuration for the specified course")
103

104
    security([%{JWT: []}])
105

106
    consumes("application/json")
107

108
    parameters do
109
      course_id(:path, :integer, "Course ID", required: true)
110
      course_name(:body, :string, "Course name")
111
      course_short_name(:body, :string, "Course module code")
112
      viewable(:body, :boolean, "Course viewability")
113
      enable_game(:body, :boolean, "Enable game")
114
      enable_achievements(:body, :boolean, "Enable achievements")
115
      enable_overall_leaderboard(:body, :boolean, "Enable overall leaderboard")
116
      enable_contest_leaderboard(:body, :boolean, "Enable contest leaderboard")
117
      top_leaderboard_display(:body, :integer, "Top Leaderboard Display")
118
      top_contest_leaderboard_display(:body, :integer, "Top Contest Leaderboard Display")
119
      enable_sourcecast(:body, :boolean, "Enable sourcecast")
120
      enable_stories(:body, :boolean, "Enable stories")
121
      enable_llm_grading(:body, :boolean, "Enable LLM grading")
122
      llm_api_key(:body, :string, "OpenAI API key for this course")
123
      sublanguage(:body, Schema.ref(:AdminSublanguage), "sublanguage object")
124
      module_help_text(:body, :string, "Module help text")
125
    end
126

127
    response(200, "OK")
128
    response(400, "Missing or invalid parameter(s)")
129
    response(403, "Forbidden")
130
  end
131

132
  swagger_path :update_assessment_configs do
1✔
133
    put("/courses/{course_id}/admin/config/assessment_configs")
134

135
    summary("Updates the assessment configuration for the specified course")
136

137
    security([%{JWT: []}])
138

139
    consumes("application/json")
140

141
    parameters do
142
      course_id(:path, :integer, "Course ID", required: true)
143
      assessment_configs(:body, :list, "Assessment Configs")
144
    end
145

146
    response(200, "OK")
147
    response(400, "Missing or invalid parameter(s)")
148
    response(403, "Forbidden")
149
  end
150

151
  def swagger_definitions do
152
    %{
1✔
153
      AdminSublanguage:
154
        swagger_schema do
1✔
155
          title("AdminSublanguage")
156

157
          properties do
1✔
158
            chapter(:integer, "Chapter number from 1 to 4",
159
              required: true,
160
              minimum: 1,
161
              maximum: 4
162
            )
163

164
            variant(Schema.ref(:SourceVariant), "Variant name", required: true)
1✔
165
          end
166

167
          example(%{
168
            chapter: 2,
169
            variant: "lazy"
170
          })
171
        end
172
    }
173
  end
174
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc