Turmas
As turmas representam os grupos de alunos vinculados a uma escola, ano letivo, série e turno. Use estes endpoints para listar, criar, editar e excluir turmas de uma escola do seu cliente.
O modelo turma
O modelo turma contém os dados de identificação da turma, capacidade de vagas e os vínculos com o
ano letivo, série e turno. Use o id
para referenciar a turma nas demais operações.
id
uuid
Identificador único da turma.
name
string
Nome da turma.
vacancies
integer
Número de vagas disponíveis na turma.
preenrollment_vacancies
integer
Número de vagas disponíveis para pré-matrícula.
school_year
string
Nome do ano letivo ao qual a turma pertence.
shift
string
Nome do turno da turma (ex: Manhã, Tarde).
grade
string
Série/ano da turma (ex: 1º ano, 2º ano).
school_cnpj
string
CNPJ da escola à qual a turma pertence.
enrollments_count
integer
Quantidade de matrículas ativas na turma.
{ "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Turma A", "vacancies": 30, "preenrollment_vacancies": 10, "school_year": "2025", "shift": "Manhã", "grade": "1º ano", "school_cnpj": "26019466000122", "enrollments_count": 25 }
/v1/partners/school/{cnpj}/classrooms/all
Listar turmas
Retorna todas as turmas de uma escola específica, identificada pelo CNPJ. A resposta é paginada,
com os registros em data.
Parâmetros de rota
cnpj
string
obrigatório
CNPJ da escola (14 dígitos, sem formatação).
Códigos de resposta
200
Sucesso.
401
Autenticação inválida.
-H "X-Authorization: {api_token}" \
-H "X-Partner: {partner_token}" \
-H "X-Client: {client_slug}"
use GuzzleHttp\Client; $client = new Client(); $response = $client->get('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classrooms/all', [ 'headers' => [ 'X-Authorization' => '{api_token}', 'X-Partner' => '{partner_token}', 'X-Client' => '{client_slug}', ], ]); $classrooms = json_decode($response->getBody(), true);
const response = await fetch('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classrooms/all', { headers: { 'X-Authorization': '{api_token}', 'X-Partner': '{partner_token}', 'X-Client': '{client_slug}', }, }); const classrooms = await response.json();
{ "data": [ { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Turma A", "vacancies": 30, "school_cnpj": "26019466000122", // ... } ], "links": { /* URLs de paginação */ }, "meta": { "current_page": 1, "per_page": 25, // ... } }
{ "message": "Autenticação inválida." }
{ "message": "Recurso não encontrado." }
/v1/partners/school/{cnpj}/classroom
Criar turma
Cria uma nova turma em uma escola do seu cliente.
⚠ Regra — Horário do turno obrigatório
Uma turma só pode ser criada para um turno se o horário daquele turno estiver completamente configurado na escola. Retorna 422 com mensagem explicativa caso o horário do turno não esteja configurado.
- Horário global: o horário de entrada do turno deve estar preenchido no horário global da escola. Ex.: turma do turno Manhã exige
morning_checkinconfigurado. - Horário semanal: o horário de entrada do turno deve estar preenchido em todos os dias ativos da semana (segunda a sexta sempre; sábado e domingo se habilitados). Se qualquer dia estiver sem horário, a criação é rejeitada com a lista de dias faltantes.
⚠ Regra — Uma disciplina por professor
Cada disciplina só pode ser atribuída a um único professor por turma. Se o campo teacher_subjects contiver a mesma disciplina para mais de um professor, a requisição será rejeitada com 400.
Parâmetros de rota
cnpj
string
obrigatório
CNPJ da escola (14 dígitos, sem formatação).
Corpo da requisição
school_year_id
uuid
obrigatório
UUID do ano letivo.
grade_id
uuid
obrigatório
UUID da série/ano.
shift_id
uuid
obrigatório
UUID do turno.
name
string
obrigatório
Nome da turma.
vacancies
integer
obrigatório
Número de vagas disponíveis.
preenrollment_vacancies
integer
opcional
Vagas para pré-matrícula. Padrão: 0.
teacher_subjects
array
opcional
Lista de professores e suas disciplinas. Cada item deve conter teacher_id (UUID do professor) e subject_ids (array de UUIDs de disciplinas). Uma disciplina não pode aparecer em mais de um professor.
Códigos de resposta
201
Criado com sucesso.
400
Dados inválidos.
401
Autenticação inválida.
404
Recurso de referência não encontrado.
-X POST \
-H "X-Authorization: {api_token}" \
-H "X-Partner: {partner_token}" \
-H "X-Client: {client_slug}" \
-H "Content-Type: application/json" \
-d '{"school_year_id":"...","grade_id":"...","shift_id":"...","name":"Turma A","vacancies":30}'
use GuzzleHttp\Client; $client = new Client(); $response = $client->post('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom', [ 'headers' => [ 'X-Authorization' => '{api_token}', 'X-Partner' => '{partner_token}', 'X-Client' => '{client_slug}', ], 'json' => [ 'school_year_id' => '{school_year_id}', 'grade_id' => '{grade_id}', 'shift_id' => '{shift_id}', 'name' => 'Turma A', 'vacancies' => 30, ], ]); $classroom = json_decode($response->getBody(), true);
const response = await fetch('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom', { method: 'POST', headers: { 'X-Authorization': '{api_token}', 'X-Partner': '{partner_token}', 'X-Client': '{client_slug}', 'Content-Type': 'application/json', }, body: JSON.stringify({ school_year_id: '{school_year_id}', grade_id: '{grade_id}', shift_id: '{shift_id}', name: 'Turma A', vacancies: 30, }), }); const classroom = await response.json();
{ "data": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Turma A", "vacancies": 30, "school_year": "2025", "shift": "Manhã", "grade": "1º ano", "school_cnpj": "26019466000122", "enrollments_count": 0 } }
{ "message": "Autenticação inválida." }
{ "message": "Recurso não encontrado." }
/v1/partners/school/{cnpj}/classroom/{id}
Editar turma
Atualiza os dados de uma turma existente. Envie apenas os campos que deseja alterar.
⚠ Regra — Uma disciplina por professor
Cada disciplina só pode ser atribuída a um único professor por turma. Se o campo teacher_subjects contiver a mesma disciplina para mais de um professor, a requisição será rejeitada com 400.
Parâmetros de rota
cnpj
string
obrigatório
CNPJ da escola (14 dígitos, sem formatação).
id
string
obrigatório
UUID da turma.
Corpo da requisição
school_year_id
uuid
opcional
UUID do ano letivo.
grade_id
uuid
opcional
UUID da série/ano.
shift_id
uuid
opcional
UUID do turno.
name
string
opcional
Nome da turma.
vacancies
integer
opcional
Número de vagas disponíveis.
preenrollment_vacancies
integer
opcional
Vagas para pré-matrícula.
teacher_subjects
array
opcional
Substitui completamente a lista de professores e disciplinas da turma. Cada item deve conter teacher_id (UUID do professor) e subject_ids (array de UUIDs de disciplinas). Omitir este campo não altera as atribuições existentes. Uma disciplina não pode aparecer em mais de um professor.
Códigos de resposta
200
Sucesso.
400
Dados inválidos.
401
Autenticação inválida.
404
Recurso não encontrado.
-X PUT \
-H "X-Authorization: {api_token}" \
-H "X-Partner: {partner_token}" \
-H "X-Client: {client_slug}" \
-H "Content-Type: application/json" \
-d '{"name":"Turma B","vacancies":35}'
use GuzzleHttp\Client; $client = new Client(); $response = $client->put('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom/{id}', [ 'headers' => [ 'X-Authorization' => '{api_token}', 'X-Partner' => '{partner_token}', 'X-Client' => '{client_slug}', ], 'json' => [ 'name' => 'Turma B', 'vacancies' => 35, ], ]);
const response = await fetch('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom/{id}', { method: 'PUT', headers: { 'X-Authorization': '{api_token}', 'X-Partner': '{partner_token}', 'X-Client': '{client_slug}', 'Content-Type': 'application/json', }, body: JSON.stringify({ name: 'Turma B', vacancies: 35 }), });
{ "data": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Turma B", "vacancies": 35, // ... } }
{ "message": "Autenticação inválida." }
{ "message": "Recurso não encontrado." }
/v1/partners/school/{cnpj}/classroom/{id}
Excluir turma
Remove uma turma da escola. A operação é irreversível.
Parâmetros de rota
cnpj
string
obrigatório
CNPJ da escola (14 dígitos, sem formatação).
id
string
obrigatório
UUID da turma.
Códigos de resposta
200
Sucesso.
401
Autenticação inválida.
404
Recurso não encontrado.
-X DELETE \
-H "X-Authorization: {api_token}" \
-H "X-Partner: {partner_token}" \
-H "X-Client: {client_slug}"
use GuzzleHttp\Client; $client = new Client(); $response = $client->delete('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom/{id}', [ 'headers' => [ 'X-Authorization' => '{api_token}', 'X-Partner' => '{partner_token}', 'X-Client' => '{client_slug}', ], ]);
const response = await fetch('https://toakiescola.com.br/api/v1/partners/school/26019466000122/classroom/{id}', { method: 'DELETE', headers: { 'X-Authorization': '{api_token}', 'X-Partner': '{partner_token}', 'X-Client': '{client_slug}', }, });
{ "success": true }
{ "message": "Autenticação inválida." }
{ "message": "Recurso não encontrado." }
/school/classroom/{id}/schedule
Obter disponibilidade da turma
Retorna a grade disponibilidade semanal de uma turma. Os períodos são agrupados por dia da semana (1 = Segunda-feira … 7 = Domingo), ordenados por posição dentro de cada dia. Cada entrada inclui o horário de início e os dados do tipo de aula associado.
Parâmetros de rota
id
uuid
obrigatório
UUID da turma.
Campos da resposta — períodos
id
uuid
Identificador único do período.
position
integer
Posição do período dentro do dia (0-based).
start_time
string
Horário de início no formato HH:MM.
class_period_type_id
uuid
UUID do tipo de aula associado.
class_period_type
object
Dados do tipo de aula: id, name, duration_minutes.
Códigos de resposta
200
Sucesso.
401
Autenticação inválida.
404
Turma não encontrada.
-H "Accept: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}"
use GuzzleHttp\Client; $client = new Client(); $response = $client->get('https://toakiescola.com.br/administrative/school/classroom/{id}/schedule', [ 'headers' => [ 'Accept' => 'application/json', 'X-XSRF-TOKEN' => '{csrf_token}', ], ]); $schedule = json_decode($response->getBody(), true);
const response = await fetch('/administrative/school/classroom/{id}/schedule', { headers: { 'Accept': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN'), }, }); const schedule = await response.json();
{ "classroom": { "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "name": "Turma A" }, "periods": { "1": [ { "id": "b2c3d4e5-...", "position": 0, "start_time": "07:00", "class_period_type_id": "c3d4e5f6-...", "class_period_type": { "id": "c3d4e5f6-...", "name": "Padrão", "duration_minutes": 50 } } ], "2": [], /* ... dias 3 a 7 ... */ } }
{ "message": "Autenticação inválida." }
{ "message": "Turma não encontrada." }
/school/classroom/{id}/schedule
Salvar disponibilidade da turma
Substitui a grade de disponibilidade semanal de uma turma de forma atômica.
Todos os períodos existentes são removidos e recriados com base no payload enviado.
O corpo deve conter um objeto periods
com as chaves 1 a
7 (dias da semana ISO:
1 = Segunda … 7 = Domingo). Dias não informados ou com array vazio são apagados.
Parâmetros de rota
id
uuid
obrigatório
UUID da turma.
Corpo da requisição
periods
object
obrigatório
Objeto com chaves 1–7. Cada chave é um array de períodos para aquele dia.
periods.*.*.start_time
string
obrigatório
Horário de início no formato HH:MM.
periods.*.*.class_period_type_id
uuid
obrigatório
UUID do tipo de aula. Deve pertencer à mesma escola da turma.
Códigos de resposta
200
Disponibilidade salva com sucesso.
422
Dados inválidos (start_time fora do formato, UUID desconhecido, tipo de aula de outra escola).
401
Autenticação inválida.
404
Turma não encontrada.
-X POST \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}" \
-d '{"periods":{"1":[{"start_time":"07:00","class_period_type_id":"c3d4..."},{"start_time":"07:50","class_period_type_id":"c3d4..."}],"2":[],"3":[],"4":[],"5":[],"6":[],"7":[]}}'
use GuzzleHttp\Client; $client = new Client(); $response = $client->post('https://toakiescola.com.br/administrative/school/classroom/{id}/schedule', [ 'headers' => [ 'Content-Type' => 'application/json', 'X-XSRF-TOKEN' => '{csrf_token}', ], 'json' => [ 'periods' => [ '1' => [ ['start_time' => '07:00', 'class_period_type_id' => 'c3d4...'], ['start_time' => '07:50', 'class_period_type_id' => 'c3d4...'], ], '2' => [], /* ... */ ], ], ]);
const response = await fetch('/administrative/school/classroom/{id}/schedule', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN'), }, body: JSON.stringify({ periods: { '1': [ { start_time: '07:00', class_period_type_id: 'c3d4...' }, { start_time: '07:50', class_period_type_id: 'c3d4...' }, ], '2': [], /* ... dias 3 a 7 */ }, }), });
{ "message": "Disponibilidade da turma salva." }
{ "message": "O horário de início deve estar no formato HH:MM.", "errors": { /* campos inválidos */ } }
{ "message": "Autenticação inválida." }
{ "message": "Turma não encontrada." }
Quadro de Horários da turma
O Quadro de Horários (timetable matrix) associa cada slot da grade semanal de uma turma a uma disciplina e a um professor. Ele é gerado automaticamente pelo sistema ou construído manualmente pelo coordenador via interface drag-and-drop.
O fluxo completo envolve cinco endpoints distintos: verificação prévia (preflight), geração automática,
leitura do estado corrente, persistência e limpeza.
Os endpoints abaixo operam sobre a rota base
/administrative/classroom-timetable/classroom/{classroomId}
e requerem autenticação de sessão com perfil de gestão escolar.
Pré-requisitos para uso do Quadro de Horários
Todos os itens abaixo devem estar configurados antes de gerar ou editar o quadro de horários de uma turma. Siga a ordem indicada — cada etapa depende das anteriores.
Forma de Avaliação do Ano Letivo
Cadastrar e configurar a forma de avaliação vigente para o ano letivo atual.
Grade de Horários (Semanal)
Definir a grade de horários semanal da escola (Horários de Entrada e Saída → modo Semanal).
Séries e Anos
Cadastrar e ordenar todas as séries/anos escolares ativos.
Disciplinas por Série
Configurar as disciplinas de cada série que participará do quadro.
Tipos de Aula
Definir os tipos de aula (ex.: Padrão 50 min, Dupla 100 min) utilizados pela escola.
Carga Horária por Série e Disciplina
Informar, para cada série e disciplina, a quantidade de aulas semanais e o tipo de aula correspondente.
Forma de Avaliação vinculada à Turma
Associar a forma de avaliação configurada (etapa 1) à turma específica.
Professores e suas Disciplinas na Turma
Vincular os professores responsáveis e suas respectivas disciplinas a cada turma.
Grade de Disponibilidade dos Professores
Cadastrar a grade de horários disponíveis de cada professor vinculado à turma.
Como funciona: heurística CSP-greedy MCV
A geração automática resolve um Problema de Satisfação de Restrições (CSP — Constraint Satisfaction Problem) usando uma heurística gulosa baseada no princípio MCV — Minimum Remaining Values (menor número de valores restantes), típico de algoritmos de busca local para coloração de grafos e escalonamento.
Variáveis e domínios
Cada lição (token de aula) é uma variável. Seu domínio é o conjunto de slots da grade semanal que satisfazem simultaneamente:
- Tipo de período correto: O tipo do slot (ex: Padrão 50 min) deve corresponder ao tipo exigido pela carga horária da disciplina.
- Professor disponível: Se o professor possuir uma grade de disponibilidade cadastrada, o slot deve estar dentro dela.
- Sem conflito de alocação: O professor não pode estar alocado no mesmo horário em outra turma do mesmo ano letivo.
- Slot livre: O slot não pode já ter sido ocupado por outra lição neste mesmo quadro.
Seleção da próxima variável — MCV
Em cada iteração o algoritmo percorre todas as lições ainda não alocadas e conta quantos slots válidos (que satisfazem todas as restrições acima) cada uma possui. A lição com menor domínio remanescente é selecionada primeiro. Isso é o MCV: priorizar as variáveis mais restritas reduz drasticamente as chances de becos sem saída mais tarde.
Em caso de empate no tamanho do domínio, o algoritmo usa a raridade do tipo de período como desempate: disciplinas que exigem um tipo de slot mais escasso são priorizadas.
Escolha do slot — espalhamento semanal
Uma vez escolhida a lição, o algoritmo busca o melhor slot dentro do domínio remanescente aplicando uma heurística de espalhamento:
- Prefere um dia diferente dos que já possuem aulas da mesma disciplina.
- Evita posições adjacentes a outras aulas da mesma disciplina no mesmo dia.
- Na ausência de uma opção ideal, aceita qualquer slot válido (fallback).
Lições não alocadas (tray)
Se nenhum slot válido existir para uma lição (por conflitos de disponibilidade, sobrecarga de professores ou insuficiência de slots do tipo correto), ela é movida para a fila de pendências de realocação (tray), acompanhada de um diagnóstico:
no_compatible_slot
Não existem slots livres do tipo correto para acomodar a lição.
teacher_unavailable
Todos os slots do tipo correto estão bloqueados pela grade de disponibilidade do professor.
teacher_double_booked
O professor já está alocado em outra turma em todos os slots compatíveis.
missing_teacher
A disciplina não possui professor vinculado à turma.
Complexidade e limites práticos
O algoritmo é O(L × S) por iteração, onde L é o número de lições pendentes e S é o número de slots. Para uma escola típica com até 40 lições semanais e 50 slots por turma isso representa menos de 2 000 comparações por iteração, tornando a geração instantânea em memória sem acesso adicional ao banco durante a fase de escalonamento. Não há retrocesso (backtracking): a heurística é puramente greedy, o que garante desempenho O(L²·S) no pior caso mas não garante solução ótima global.
// Expandir carga horária em tokens de lição lessons = expandWorkload(workloads) unplaced = lessons takenSlots = {} teacherBusy = busyAcrossClassrooms placed = [] unfilled = [] while unplaced not empty: // ① MCV: escolher a lição com menor domínio remanescente best = null; bestScore = ∞ for lesson in unplaced: count = countValidSlots(lesson, slots, takenSlots, teacherBusy) score = count × 10000 + slotRarity(lesson.type) if score < bestScore: best = lesson if best == null: break // ② escolher slot com espalhamento semanal slot = pickBestSlot(best, slots, placedBySubjectDay) if slot == null: unfilled.push(best + 'reason': diagnose(best)) else: placed.push({ slot, subject, teacher }) takenSlots.add(slot.id) teacherBusy.add(teacher|day|time) return { placed, unfilled }
/classroom-timetable/classroom/{classroomId}/preflight
Verificação prévia (preflight)
Executa a validação de pré-condições antes de gerar o quadro. Retorna uma lista estruturada de problemas que, se presentes, impedirão a geração automática. Útil para exibir avisos antecipados ao usuário. Não altera nenhum dado.
Parâmetros de rota
classroomId
uuid
obrigatório
UUID da turma a verificar.
Campos da resposta
ok
boolean
true quando não há nenhum problema. false quando há pelo menos um item em issues.
issues
array
Lista de objetos descrevendo cada problema encontrado. Vazio quando ok = true.
issues[].code
string
Código de erro: NO_SCHEDULE, NO_WORKLOAD, WORKLOAD_NO_ITEMS, MISSING_TEACHER, NO_MATCHING_SLOT_TYPE, DEMAND_EXCEEDS_CAPACITY.
issues[].message
string
Descrição legível do problema, adequada para exibição ao usuário.
Códigos de problema
NO_SCHEDULE
A turma não possui nenhum slot de horário cadastrado.
NO_WORKLOAD
A série da turma não possui carga horária definida.
WORKLOAD_NO_ITEMS
Uma disciplina existe na carga horária mas não possui itens (quantidade × tipo).
MISSING_TEACHER
Uma disciplina requerida não possui professor vinculado a esta turma.
NO_MATCHING_SLOT_TYPE
Não existem slots do tipo de período exigido por uma disciplina.
DEMAND_EXCEEDS_CAPACITY
O total de aulas requeridas de um tipo excede a quantidade de slots daquele tipo disponíveis na grade.
Códigos de resposta HTTP
200
Verificação executada (mesmo que haja issues).
401
Autenticação inválida.
404
Turma não encontrada ou não pertence à escola do usuário.
-H "Accept: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}"
$response = $client->get('.../classroom-timetable/classroom/{id}/preflight', [ 'headers' => ['Accept' => 'application/json', 'X-XSRF-TOKEN' => '{csrf}'], ]);
const r = await fetch('/administrative/classroom-timetable/classroom/{id}/preflight', { headers: { 'Accept': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') }, }); const result = await r.json();
{ "ok": true, "issues": [] }
{ "ok": false, "issues": [ { "code": "MISSING_TEACHER", "message": "A disciplina 'Matemática' não possui professor vinculado a esta turma." } ] }
/classroom-timetable/classroom/{classroomId}
Obter quadro de horários
Retorna o estado completo do quadro de horários de uma turma: slots da grade semanal, entradas já alocadas, disciplinas, professores, pendências de realocação (tray), mapa de carga horária por bucket e mapa de conflitos cruzados entre turmas.
Campos da resposta
classroom
object
Dados da turma: id, name, school_year, shift, grade.
slots
array
Todos os slots da grade semanal da turma. Cada item contém id, day_of_week (1–7), position (1-based), start_time (HH:MM), class_period_type_id e o objeto class_period_type.
entries
array
Aulas já alocadas. Cada item: schedule_period_id, subject_id, teacher_id.
tray
array
Lições sem slot. Cada item: subject_id, class_period_type_id. Itens obrigatórios (presentes na carga horária e ainda não satisfeitos) bloqueiam o salvamento.
workload_buckets
object
Mapa "subjectId|classPeriodTypeId" → quantidade total exigida pela carga horária. Permite distinguir pendências obrigatórias de extras no tray.
subjects
array
Disciplinas envolvidas: id, name, abbreviation, color (hex #RRGGBB ou null).
teachers
array
Professores da turma: id, name.
teacher_subject
array
Vínculos professor–disciplina para esta turma: subject_id, teacher_id.
cross_busy
array
Horários em que professores da turma já estão alocados em outras turmas no mesmo ano letivo: teacher_id, day_of_week, start_time.
Códigos de resposta HTTP
200
Sucesso.
401
Autenticação inválida.
404
Turma não encontrada ou não pertence à escola do usuário.
-H "Accept: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}"
$response = $client->get('.../classroom-timetable/classroom/{id}', [ 'headers' => ['Accept' => 'application/json', 'X-XSRF-TOKEN' => '{csrf}'], ]);
const r = await fetch('/administrative/classroom-timetable/classroom/{id}', { headers: { 'Accept': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') }, });
{ "classroom": { "id": "a1b2...", "name": "1062", ... }, "slots": [ { "id": "b2c3...", "day_of_week": 1, "position": 1, "start_time": "07:00", ... } ], "entries": [ { "schedule_period_id": "b2c3...", "subject_id": "c3d4...", "teacher_id": "d4e5..." } ], "tray": [], "workload_buckets": { "c3d4...|type-uuid": 4 }, "subjects": [{ "id": "c3d4...", "name": "Matemática", "color": "#a855f7" }], "teachers": [{ "id": "d4e5...", "name": "Prof. João" }], "teacher_subject": [{ "subject_id": "c3d4...", "teacher_id": "d4e5..." }], "cross_busy": [] }
/classroom-timetable/classroom/{classroomId}/generate
Gerar quadro automaticamente
Executa a heurística CSP-greedy MCV e retorna uma proposta de alocação sem persistir nenhum dado. O resultado deve ser revisado e, se aprovado, enviado ao endpoint de salvamento. Se o preflight retornar problemas, este endpoint também responderá com 422.
Não há corpo de requisição. A geração é baseada inteiramente nos dados já cadastrados (grade, carga horária, vínculos professor–disciplina, disponibilidade e conflitos cruzados).
Campos da resposta
entries
array
Aulas alocadas pelo algoritmo. Cada item: schedule_period_id (slot UUID), subject_id, teacher_id, day_of_week, position, start_time.
unfilled
array
Lições que não puderam ser alocadas. Cada item: subject_id, class_period_type_id, teacher_id, reason (string).
unfilled[].reason
string
Diagnóstico: no_compatible_slot, teacher_unavailable, teacher_double_booked ou missing_teacher.
Códigos de resposta HTTP
200
Proposta gerada. Pode conter itens em unfilled.
422
Pré-condições não satisfeitas. Contém issues com os mesmos códigos do preflight.
401
Autenticação inválida.
-X POST \
-H "Accept: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}"
$response = $client->post('.../classroom-timetable/classroom/{id}/generate', [ 'headers' => ['Accept' => 'application/json', 'X-XSRF-TOKEN' => '{csrf}'], ]);
const r = await fetch('/administrative/classroom-timetable/classroom/{id}/generate', { method: 'POST', headers: { 'Accept': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') }, });
{ "entries": [ { "schedule_period_id": "b2c3d4e5-...", "subject_id": "c3d4e5f6-...", "teacher_id": "d4e5f6a7-...", "day_of_week": 1, "position": 2, "start_time": "07:50" } ], "unfilled": [] }
{ "message": "Não é possível gerar o quadro de horários.", "issues": [{ "code": "NO_WORKLOAD", "message": "..." }] }
/classroom-timetable/classroom/{classroomId}
Salvar quadro de horários
Persiste o quadro de horários de forma atômica: todas as entradas existentes são apagadas e substituídas pelas enviadas no payload. Executa validações adicionais antes de gravar:
- Cada entrada deve referenciar um slot válido da turma.
- Cada entrada deve ter o professor vinculado à disciplina nesta turma.
- O tipo de período do slot deve corresponder ao da carga horária da disciplina.
- O tray não pode conter itens obrigatórios (aqueles cobertos pela carga horária e ainda insuficientemente alocados) — retorna 422.
-
Conflitos cruzados (professor em outra turma no mesmo horário) retornam 409 e podem ser forçados com
force: true. - Lições com déficit em relação à carga horária retornam 409 e também podem ser forçadas.
Corpo da requisição
entries
array
obrigatório
Lista das entradas a persistir.
entries[].schedule_period_id
uuid
obrigatório
UUID do slot (ClassroomSchedulePeriod) desta entrada.
entries[].subject_id
uuid
obrigatório
UUID da disciplina alocada neste slot.
entries[].teacher_id
uuid
obrigatório
UUID do professor. Deve estar vinculado à disciplina nesta turma.
tray
array
obrigatório
Lições pendentes de realocação. Itens obrigatórios (cobertos pela carga horária) bloqueiam o salvamento.
tray[].subject_id
uuid
obrigatório
UUID da disciplina pendente.
tray[].class_period_type_id
uuid|null
opcional
UUID do tipo de período. null para disciplinas adicionadas manualmente sem restrição de tipo.
force
boolean
opcional
Quando true, ignora avisos de conflito cruzado (409) e déficit de carga horária (409). Não bypassa os erros de validação dura (422).
Códigos de resposta HTTP
200
Quadro salvo com sucesso.
409
Conflito de professor entre turmas ou déficit de carga horária. Pode ser ignorado com force: true.
422
Dados inválidos ou pendências obrigatórias no tray. Não pode ser forçado.
401
Autenticação inválida.
-X PUT \
-H "Content-Type: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}" \
-d '{"entries":[{"schedule_period_id":"b2c3...","subject_id":"c3d4...","teacher_id":"d4e5..."}],"tray":[],"force":false}'
$response = $client->put('.../classroom-timetable/classroom/{id}', [ 'headers' => ['Content-Type' => 'application/json', 'X-XSRF-TOKEN' => '{csrf}'], 'json' => [ 'entries' => [[ 'schedule_period_id' => 'b2c3...', 'subject_id' => 'c3d4...', 'teacher_id' => 'd4e5...', ]], 'tray' => [], 'force' => false, ], ]);
const r = await fetch('/administrative/classroom-timetable/classroom/{id}', { method: 'PUT', headers: { 'Content-Type': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') }, body: JSON.stringify({ entries: [{ schedule_period_id: 'b2c3...', subject_id: 'c3d4...', teacher_id: 'd4e5...' }], tray: [], force: false, }), });
{ "message": "Quadro de horários salvo." }
{ "message": "O professor já está alocado em outra turma neste horário.", "conflicts": [0, 3] // índices em entries[] }
{ "message": "Há disciplinas obrigatórias pendentes de realocação." }
/classroom-timetable/classroom/{classroomId}
Limpar quadro de horários
Remove todas as entradas do quadro de horários da turma. A operação é irreversível. Não há corpo de requisição.
Códigos de resposta HTTP
200
Quadro removido com sucesso.
401
Autenticação inválida.
404
Turma não encontrada.
-X DELETE \
-H "Accept: application/json" \
-H "X-XSRF-TOKEN: {csrf_token}"
$response = $client->delete('.../classroom-timetable/classroom/{id}', [ 'headers' => ['Accept' => 'application/json', 'X-XSRF-TOKEN' => '{csrf}'], ]);
const r = await fetch('/administrative/classroom-timetable/classroom/{id}', { method: 'DELETE', headers: { 'Accept': 'application/json', 'X-XSRF-TOKEN': getCookie('XSRF-TOKEN') }, });
{ "message": "Quadro de horários removido." }
{ "message": "Autenticação inválida." }