Sobre alunos da primeira série, ensino à distância e programação assíncrona

imagem



Eu sou um downshifter. Acontece que há três anos minha esposa e eu apreciamos a paisagem rural fora da janela, o ar puro e o canto dos pássaros. Conveniências em casa, Internet óptica de um provedor local, uma poderosa fonte de alimentação ininterrupta e uma cobiça que de repente apareceu, de repente tornaram a ideia de mudar de uma metrópole não tão estranha.



Enquanto eu estava entusiasmado com o desenvolvimento da web, em algum lugar no fundo, minha esposa reclamava periodicamente sobre os problemas de escolher uma escola para meu filho. E então (de repente) a criança cresceu e a questão da escola se levantou. Ok, então chegou a hora. Vamos descobrir juntos, o que há de errado com o sistema educacional no antigo 1/6 das terras e o que podemos fazer a respeito?



Deixarei os métodos tradicionais de ensino presencial fora do escopo deste artigo. Direi apenas que as escolas normais têm vantagens indiscutíveis e sérias desvantagens, às quais, a propósito, foi adicionado recentemente o auto-isolamento forçado. Aqui, examinamos as opções de educação à distância e para a família que, por diversos motivos, têm atraído cada vez mais pais recentemente.



Para ser claro: o ensino à distância implica aulas em uma escola regular usando “tecnologias de ensino à distância” (DOT), e educação familiar significa abandono voluntário da escola e aprendizagem apenas pela família (na verdade, este é um bom e velho estágio externo). No entanto, em qualquer caso, a criança deve ser matriculada em qualquer uma das escolas disponíveis, pelo menos para passar os atestados intermediários.



E agora algumas observações da vida. Com a transferência forçada para o ensino a distância de crianças que já estudaram em escola regular, está tudo triste. As crianças em idade escolar percebem esse presente do destino como uma espécie de férias, os pais não estão acostumados a seguir a disciplina durante as aulas e, como resultado, o desempenho acadêmico geral inevitavelmente cai.



Com os alunos da primeira série, principalmente no caso da forma familiar, os pais, talvez, tenham a chance de colocar a criança “na berlinda” usando o interesse natural e o efeito da novidade. Para mim, pessoalmente, alcançar a independência é a tarefa principal. Sentado e fazendo lição de casa com uma criança, considero o cúmulo da estupideznão totalmente razoável. Claro, se você quiser que seus filhos realizem algo na vida e não pendurem em seu pescoço. Quero, portanto, meu objetivo é ensinar uma criança a aprender, fazer perguntas corretamente e, em geral, pensar com a própria cabeça.



Vá direto ao ponto. Escolhendo uma escola pública



Talvez eu goste mais da educação familiar pela oportunidade de escolher um programa e um cronograma de treinamento. E você pode ir fisicamente à escola com menos frequência. Mas é preciso escolher uma escola pública, conversar com a diretora sobre a colocação da criança e receber um pedido de admissão à primeira série no final do inverno para que não haja surpresas em setembro. Embora, do ponto de vista jurídico, a lei da educação não pareça exigir atestados anuais, os "prazos", na minha experiência, são excelentes motivadores, portanto, que haja atestados. É improvável que alguma escola nos aceite de braços abertos, mas tenho certeza de que encontraremos uma opção decente na cidade mais próxima.



Escolhendo um currículo



Nós escolhemos exatamente. Tentar compor um programa sozinho sem ter uma formação especializada não é razoável. Embora existam recursos educacionais do governo, como a Escola Eletrônica Russa ( NES ) e a Escola Eletrônica de Moscou ( MES ), que em teoria seriam suficientes, mas ... Ambas as opções oferecem planos de aula, vídeos, testes e tutoriais. O que não consegui encontrar foram os próprios livros didáticos, mesmo para o currículo obrigatório.



E aqui falta o mais importante: comunicação. Ensinar uma criança mostrando-lhe inúmeros vídeos e forçando-a a passar nos testes não funcionará. Portanto, você precisa conduzir as aulas de forma totalmente independente ou escolher uma das escolas online.



Escolhendo uma escola online



Estamos quase de volta ao ponto de partida. Controle remoto? Ok, vamos dar uma olhada nela. Como você pode organizar o processo educacional remotamente? Isso levanta muitas questões, só levantarei as principais:



* Comunicação ao vivo. O que as escolas oferecem? Skype, Tims na melhor das hipóteses. Aulas do Skype? Mesmo? Se não me engano, é 2020. Abrir várias janelas com belos botões multicoloridos na frente do aluno da primeira série e esperar que ele não os pressione, mas escute obedientemente um tio ou tia chata por meio dia? Nunca vi crianças assim. E você?



* Dever de casa. Mais precisamente, como chega ao professor para o teste? Na verdade, esta é uma questão muito difícil, talvez nem mesmo resolvida em princípio. Opções existentes:



  1. , . --, , , , - .

  2. . , - .

  3. . , .

  4. . , , , , ? . , , .

  5. . , , . , , , . , . . , , , , . .



* Estimativas. Obviamente, as notas dadas na aula e na verificação do dever de casa devem cair em um diário eletrônico à disposição dos pais. E eles chegam lá. Mas não imediatamente. Perguntei a crianças mais velhas que se formaram em um dos liceus de prestígio com uma cúpula de ouro (ironicamente, com um viés informacional), por que isso? A resposta, para ser honesto, me surpreendeu. Acontece que os professores anotam as notas em um pedaço de papel e, depois das aulas, eles as colocam neste diário eletrônico no portal estadual. E isso enquanto Tesla Elon Musk ara a vastidão do espaço ...



Ok, é hora de fazer uma pequena pesquisa técnica e verificar se há razões objetivas para esse estado de coisas?



Vamos definir os requisitos para uma plataforma de aprendizado ideal hipotética. Na verdade, tudo é simples: a criança deve permanecer na aula, concentrando-se no que o professor fala e mostrando, respondendo às perguntas se necessário e levantando a mão se desejar. Basicamente, queremos uma janela em tela cheia com um fluxo da câmera, apresentação ou quadro branco do professor. A maneira mais fácil de conseguir isso é usar a tecnologia WebRTC(comunicações em tempo real, comunicações em tempo real). Funciona em qualquer navegador mais ou menos moderno, dispensa a compra de equipamentos adicionais e, além disso, proporciona uma conexão de boa qualidade. E sim, este padrão requer programação assíncrona, pelo menos porque o método JS necessário navigator.mediaDevices.getUserMedia () retorna uma promessa . Tudo parece claro, estou começando a implementar.



Digressão lírica sobre a escolha de uma estrutura
, «» JavaScript , . jQuery. , JS :



//  
element = $(selector);
element = document.querySelector(selector);

//    
element2 = element.find(selector2);
element2 = element.querySelector(selector2);

//  
element.hide();  //   display: none
element.classList.add('hidden');

      
      





, CSS «hidden», , opacity transition, fadeIn/fadeOut CSS. , JS !



//   onClick
element.click(e => { ... });
element.onclick = (e) => { ...  }

//  
element.toggleClass(class_name);
element.classList.toggle(class_name);

//  div
div = $("<div>");
div = document.createElement("div");

//   div  element
// (  ,   )
element.append(div);
element.append(div);

      
      





. .. , JS , . , , «» JS !



WebRTC é projetado para comunicação direta entre navegadores, usando tecnologia ponto a ponto (p2p). No entanto, para estabelecer essa conexão, os navegadores devem se informar sobre sua intenção de se comunicar. Isso requer um servidor de alarme .



Um exemplo de implementação básica de um chat de vídeo simples usando a topologia "full mesh"
'use strict';

(function () {
    const selfView = document.querySelector('#self-view'),
        remoteMaster = document.querySelector('#remote-master'),
        remoteSlaves = document.querySelector('#remote-slaves');

    let localStream,
        selfStream = null,
        socket = null,
        selfId = null,
        connections = {};

    // ***********************
    // UserMedia & DOM methods
    // ***********************

    const init = async () => {
        try {
            let stream = await navigator.mediaDevices.getUserMedia({
                audio: true, video: {
                    width: { max: 640 }, height: { max: 480 }
                }
            });
            localStream = stream;

            selfStream = new MediaStream();

            stream.getVideoTracks().forEach(track => {
                selfStream.addTrack(track, stream); // track.kind == 'video'
            });
            selfView.querySelector('video').srcObject = selfStream;

        } catch (e) {
            document.querySelector('#self-view').innerHTML =
                '<i>     </i>';
            console.error('Local stream not found: ', e);
        }
        wsInit();
    }

    const createRemoteView = (id, username) => {
        let iDiv = document.querySelector('#pc' + id);
        if (!iDiv) {
            iDiv = document.createElement('div');
            iDiv.className = 'remote-view';
            iDiv.id = 'pc' + id;

            let iVideo = document.createElement('video');
            iVideo.setAttribute('autoplay', 'true');
            iVideo.setAttribute('playsinline', 'true');

            let iLabel = document.createElement('span');

            iDiv.append(iVideo);
            iDiv.append(iLabel);

            if (!remoteMaster.querySelector('video')) {
                remoteMaster.append(iDiv);
                iLabel.textContent = '';
            } else {
                remoteSlaves.append(iDiv);
                iLabel.textContent = username;
            }
            remoteMaster.style.removeProperty('display');
        }
    }

    // *******************************
    // Signaling (Web Socket) methods
    // *******************************

    const wsInit = () => {
        socket = new WebSocket(SIGNALING_SERVER_URL);

        socket.onopen = function (e) {
            log('[socket open]  ');
        }

        socket.onmessage = function (event) {
            log('[socket message]    ', event);

            wsHandle(event.data);
        }

        socket.onclose = function (event) {
            if (event.wasClean) {
                log('[close]   , ' +
                    `=${event.code} =${event.reason}`);
            } else {
                log('[socket close]  ', event);
            }
            clearInterval(socket.timer);
        }

        socket.onerror = function (error) {
            logError('[socket error]', error);
        }

        socket.timer = setInterval(() => {
            socket.send('heartbeat');
        }, 10000);
    }

    const wsHandle = async (data) => {
        if (!data) {
            return;
        }
        try {
            data = JSON.parse(data);
        } catch (e) {
            return;
        }

        switch (data.type) {
            case 'handshake':
                selfId = data.uid;
                if (!Object.keys(data.users).length) {
                    createRemoteView(selfId, '');
                    remoteMaster.querySelector('video').srcObject =
                        selfStream;
                    selfView.remove();
                    break;
                } else {
                    selfView.style.removeProperty('display');
                }
                for (let id in data.users) {
                    await pcCreate(id, data.users[id]);
                }
                break;
            case 'offer':
                await wsHandleOffer(data);
                break;
            case 'answer':
                await wsHandleAnswer(data)
                break;
            case 'candidate':
                await wsHandleICECandidate(data);
                break;
            default:
                break;
        }
    }

    const wsHandleOffer = async (data) => {
        let pc = null;

        if (!connections[data.src]) {
            await pcCreate(data.src, data.username);
        }

        pc = connections[data.src].pc;

        // We need to set the remote description to the received SDP offer
        // so that our local WebRTC layer knows how to talk to the caller.
        let desc = new RTCSessionDescription(data.sdp);

        pc.setRemoteDescription(desc).catch(error => {
            logError('handleOffer', error);
        });

        await pc.setLocalDescription(await pc.createAnswer());

        wsSend({
            type: 'answer',
            target: data.src,
            sdp: pc.localDescription
        });

        connections[data.src].pc = pc; // ???
    }

    const wsHandleAnswer = async (data) => {
        log('*** Call recipient has accepted our call, answer:', data);

        let pc = connections[data.src].pc;

        // Configure the remote description,
        // which is the SDP payload in our 'answer' message.

        let desc = new RTCSessionDescription(data.sdp);
        await pc.setRemoteDescription(desc).catch((error) => {
            logError('handleAnswer', error);
        });
    }

    const wsHandleICECandidate = async (data) => {
        let pc = connections[data.src].pc;

        let candidate = new RTCIceCandidate(data.candidate);

        log('*** Adding received ICE candidate', candidate);

        pc.addIceCandidate(candidate).catch(error => {
            logError('handleICECandidate', error);
        });
    }

    const wsSend = (data) => {
        if (socket.readyState !== WebSocket.OPEN) {
            return;
        }
        socket.send(JSON.stringify(data));
    }

    // ***********************
    // Peer Connection methods
    // ***********************

    const pcCreate = async (id, username) => {
        if (connections[id]) {
            return;
        }
        try {
            let pc = new RTCPeerConnection(PC_CONFIG);

            pc.onicecandidate = (event) =>
                pcOnIceCandidate(event, id);
            pc.oniceconnectionstatechange = (event) =>
                pcOnIceConnectionStateChange(event, id);
            pc.onsignalingstatechange =  (event) =>
                pcOnSignalingStateChangeEvent(event, id);
            pc.onnegotiationneeded = (event) =>
                pcOnNegotiationNeeded(event, id);
            pc.ontrack = (event) =>
                pcOnTrack(event, id);

            connections[id] = {
                pc: pc,
                username: username
            }

            if (localStream) {
                try {
                    localStream.getTracks().forEach(
                        (track) => connections[id].pc.addTransceiver(track, {
                            streams: [localStream]
                        })
                    );
                } catch (err) {
                    logError(err);
                }
            } else {
                // Start negotiation to listen remote stream only
                pcOnNegotiationNeeded(null, id);
            }
            createRemoteView(id, username);
        } catch (error) {
            logError('Peer: Connection failed', error);
        }
    }

    const pcOnTrack = (event, id) => {
        let iVideo = document.querySelector('#pc' + id + ' video');
        iVideo.srcObject = event.streams[0];
    }

    const pcOnIceCandidate = (event, id) => {
        let pc = connections[id].pc;

        if (event.candidate && pc.remoteDescription) {
            log('*** Outgoing ICE candidate: ' + event.candidate);
            wsSend({
                type: 'candidate',
                target: id,
                candidate: event.candidate
            });
        }
    }

    const pcOnNegotiationNeeded = async (event, id) => {
        let pc = connections[id].pc;
        try {
            const offer = await pc.createOffer();

            // If the connection hasn't yet achieved the "stable" state,
            // return to the caller. Another negotiationneeded event
            // will be fired when the state stabilizes.
            if (pc.signalingState != 'stable') {
                return;
            }

            // Establish the offer as the local peer's current
            // description.
            await pc.setLocalDescription(offer);

            // Send the offer to the remote peer.
            wsSend({
                type: 'offer',
                target: id,
                sdp: pc.localDescription
            });
        } catch(err) {
            logError('*** The following error occurred while handling' +
                ' the negotiationneeded event:', err);
        };
    }

    const pcOnIceConnectionStateChange = (event, id) => {
        let pc = connections[id].pc;
        switch (pc.iceConnectionState) {
            case 'closed':
            case 'failed':
            case 'disconnected':
                pcClose(id);
                break;
        }
    }

    const pcOnSignalingStateChangeEvent = (event, id) => {
        let pc = connections[id].pc;

        log('*** WebRTC signaling state changed to: ' + pc.signalingState);

        switch (pc.signalingState) {
            case 'closed':
                pcClose(id);
                break;
        }
    }

    const pcClose = (id) => {
        let remoteView = document.querySelector('#pc' + id);

        if (connections[id]) {
            let pc = connections[id].pc;
            pc.close();
            delete connections[id];
        }
        if (remoteView) {
            remoteView.remove();
        }
    }

    // *******
    // Helpers
    // *******

    const log = (msg, data) => {
        if (!data) {
            data = ''
        }
        console.log(msg, data);
    }

    const logError = (msg, data) => {
        if (!data) {
            data = ''
        }
        console.error(msg, data);
    }

    init();
})();

      
      







O servidor de sinalização é feito na estrutura Python aiohttp e é uma "visualização" simples que faz proxy de solicitações WebRTC trivialmente. A conexão com o servidor neste exemplo é feita através de sockets da web . Bem, além disso, os dados do chat de texto simples são transmitidos através do canal de sinalização.



Exemplo de implementação de servidor de sinalização
import json
from aiohttp.web import WebSocketResponse, Response
from aiohttp import WSMsgType
from uuid import uuid1
from lib.views import BaseView


class WebSocket(BaseView):
    """ Process WS connections """

    async def get(self):
        username = self.request['current_user'].firstname or ''

        room_id = self.request.match_info.get('room_id')

        if room_id != 'test_room' and
            self.request['current_user'].is_anonymous:
            self.raise_error('forbidden')  # @TODO: send 4000

        if (self.request.headers.get('connection', '').lower() != 'upgrade' or
            self.request.headers.get('upgrade', '').lower() != 'websocket'):
            return Response(text=self.request.path)  # ???

        self.ws = WebSocketResponse()
        await self.ws.prepare(self.request)

        self.uid = str(uuid1())

        if room_id not in self.request.app['web_sockets']:
            self.request.app['web_sockets'][room_id] = {}

        self.room = self.request.app['web_sockets'][room_id]

        users = {}
        for id, data in self.room.items():
            users[id] = data['name']

        ip = self.request.headers.get(
            'X-FORWARDED-FOR',
            self.request.headers.get('X-REAL-IP',
            self.request.remote))

        msg = {
            'type': 'handshake',
            'uid': str(self.uid),
            'users': users, 'ip': ip}
        await self.ws.send_str(json.dumps(msg, ensure_ascii=False))

        self.room[self.uid] = {'name': username, 'ws': self.ws}

        try:
            async for msg in self.ws:
                if msg.type == WSMsgType.TEXT:
                    if msg.data == 'heartbeat':
                        print('---heartbeat---')
                        continue

                    try:
                        msg_data = json.loads(msg.data)

                        if 'target' not in msg_data or
                            msg_data['target'] not in self.room:
                            continue

                        msg_data['src'] = self.uid

                        if 'type' in msg_data and 'target' in msg_data:
                            if msg_data['type'] == 'offer':
                                msg_data['username'] = username
                        else:
                            print('INVALID DATA:', msg_data)
                    except Exception as e:
                        print('INVALID JSON', e, msg)

                    try:
                        await self.room[msg_data['target']]['ws'].send_json(
                            msg_data);
                    except Exception as e:
                        if 'target' in msg_data:
                            self.room.pop(msg_data['target'])

        finally:
            self.room.pop(self.uid)

        return self.ws

      
      







A tecnologia WebRTC, além da comunicação por vídeo, permite que você dê permissão ao navegador para capturar o conteúdo de um display ou de um aplicativo separado, o que pode ser indispensável ao conduzir aulas, webinars ou apresentações online. Ótimo, vamos usar.



Fiquei tão entusiasmado com as possibilidades modernas da videocomunicação que quase me esqueci do assunto mais importante da aula - a lousa interativa. No entanto, a implementação básica é tão trivial que não vou sobrecarregar este artigo com ela. Apenas adicionamos a tela , ouvimos os eventos de movimento do mouse onmousemove (ontouchmove para tablets) e enviamos as coordenadas recebidas a todos os pontos conectados através do mesmo servidor de sinalização.



Testando seu quadro interativo



Aqui você precisa de um tablet, um digitalizador e uma criança viva. Ao mesmo tempo, verificaremos a possibilidade de digitalizar a entrada de caligrafia.



Para começar, peguei um velho tablet Galaxy Tab com Android 4.4, uma caneta especial e os primeiros cadernos que encontrei como fundo para a tela. Não instalei programas adicionais. O resultado me desanimou: meu tablet absolutamente não é adequado para escrever! Ou seja, mover o dedo sobre ela não é problema, mas colocar a caneta no contorno de uma letra, mesmo uma tão grande como na imagem abaixo, já é um problema. Além disso, o gadget começa a perder o brilho no processo de desenho, resultando em linhas quebradas. Além disso, não consegui fazer com que a criança não apoiasse o pulso na tela, o que deixa manchas adicionais à mão e o próprio tablet começa a ficar ainda mais lento. Resumindo: um tablet comum para escrever em um quadro branco não é adequado. O máximo de suas capacidades é mover o dedo pela tela figuras bastante grandes. Mas é tarde demais para oferecer isso aos alunos.



Ok, esta é uma pesquisa puramente teórica, certo? Em seguida, pegamos um digitalizador (também conhecido como tablet gráfico) formato Wacom Bamboo A8 e observamos a criança.



Observe que meu sujeito de teste de seis anos recebeu um laptop com uma caneta gráfica pela primeira vez na vida. Demoramos dez minutos para adquirir as habilidades básicas de uso da caneta, e já na segunda aula a criança usava o tablet com bastante segurança, apagava de forma independente do quadro, desenhava rostos, flores, nosso cachorro e até começou a cutucar botões disponíveis em épsilon, simultaneamente fazendo perguntas como "Por que eles levantam a mão na escola?" Mas o resultado sempre deixou muito a desejar. O fato é que designers e artistas maximizam um fragmento da imagem para desenhar um elemento, o que torna as linhas precisas. Aqui devemos ver todo o tabuleiro, na escala de 1: 1. Aqui e o adulto não vai cair na linha. Aqui está o que temos:



imagem



Veredicto final: nenhuma caligrafia está fora de questão. E se queremos “colocar a mão” nos nossos filhos, temos que conseguir sozinhos, no papel, a escola não vai ajudar nisso.



Devo dizer que a criança levou todas as minhas experiências com entusiasmo e, além disso, desde então tem me seguido com o rabo e a pedir-me para “ligar as receitas”. Já bem, a habilidade adquirida será útil para ele, apenas para finalidades completamente diferentes.



De qualquer forma, como resultado de experimentos, eu realmente consegui um MVP - um produto mínimo viável, quaseadequado para aulas online, com vídeo / audioconferência, tela compartilhada, quadro branco interativo, chat de texto simples e botão para levantar a mão. Isso se a criança de repente não tiver um microfone. Sim, isso acontece, principalmente entre crianças que não aprenderam as lições.



Mas neste barril de mel, infelizmente, há um par de colheres de alcatrão.



Testando WebRTC



Colher número 1. Visto que nossa comunicação por vídeo usa conexões diretas entre clientes, a primeira etapa é testar a escalabilidade de tal solução. Para o teste, peguei um laptop antigo com um i5-3230M dual-core a bordo e comecei a conectar clientes com câmeras da web desativadas, ou seja, emulando um modo um para muitos:



imagem



Como você pode ver, o laptop experimental é capaz de transmitir mais ou menos confortavelmente para cinco clientes ( com carga da CPU em 60%). E isso é fornecido, a resolução do fluxo de vídeo de saída é reduzida para 720p (640x480px) e a taxa de quadros para 15 fps. Em princípio, não é tão ruim, mas ao conectar uma turma de várias dezenas de alunos, você terá que abandonar a "malha completa" em favor da cascata, ou seja, cada um dos primeiros cinco clientes faz o proxy do fluxo para os próximos cinco e assim por diante.



Colher número 2.Para criar uma conexão direta interativa (ICE) entre clientes, eles precisam contornar firewalls e NATs. Para fazer isso, o WebRTC usa um servidor STUN , que informa os clientes sobre os parâmetros de conexão externa. Acredita-se que na maioria dos casos isso seja suficiente. Mas quase imediatamente tive "sorte":



Como você pode ver, o depurador reclama da impossibilidade de conexão ICE e requer a conexão de um servidor TURN, ou seja, um relé. E isso já é CARO. Um servidor de sinalização simples é indispensável aqui. Conclusão - você deve passar todos os fluxos pelo servidor de mídia.



Servidor de mídia



Para testes, usei aiortc . Um desenvolvimento interessante, ele permite que você conecte o navegador diretamente ao servidor via WebRTC. A sinalização separada não é necessária, você pode usar o canal de dados da própria conexão. Isso funciona, todos os meus pontos de teste conectados sem problemas. Mas o problema com o desempenho. Um simples eco de um stream de vídeo / áudio com os mesmos limites de 720p e 15fps consumiu 50% da minha CPU virtual no VDS de teste. Além disso, se a carga for aumentada para 100%, o stream de vídeo não tem tempo para ser descarregado para os clientes e começa a obstruir a memória, o que acaba levando a uma parada do servidor. Obviamente, o Python que amamos usar para processamento de E / S não é muito limitado pela CPU. Teremos que procurar uma solução mais especializada, por exemplo, Janusou Jitsy .



Em qualquer caso, uma solução completa exigirá servidores dedicados, de acordo com minhas estimativas, à taxa de 1 núcleo por sala (classe). Isso já custa dinheiro e vai além de um simples teste, portanto, neste ponto, considerarei concluída a primeira fase da minha pesquisa.



conclusões



1. Para dizer o mínimo, é estranho ver instruções de download e links para se registrar no programa do ex-inimigo em potencial número 1 (aqui sobre o Microsoft Teams) no portal oficial da Federação Russa . E estamos na era das sanções e da substituição de importações.

Não, pessoalmente sou a favor da amizade dos povos e, em geral, de todo tipo de tolerância, mas sou realmente só eu dessa "integração" que fico com os cabelos em pé? Não existem nossos desenvolvimentos?



2. Integração do MES / NES com as escolas. Na verdade os desenvolvedores do MES são ótimos, até fizeram integração com o Yandex.tutor. E a avaliação em tempo real durante as aulas, quando haverá uma API? Ou não estou ciente de algo?



3. Ao escolher uma forma de educação à distância ou familiar, você precisa ser honesto consigo mesmo: não funcionará transferir a responsabilidade pela educação de seu filho para a escola. Todo o trabalho de condução das aulas (no caso da educação familiar), manutenção da disciplina e da auto-organização (em qualquer caso) recai inteiramente sobre os pais. Você precisa estar ciente disso e encontrar tempo para as aulas. No entanto, em famílias grandes, isso não deve ser um problema.



4. Não fornecerei links para escolas online selecionadas aqui, para que não sejam considerados um anúncio. Direi apenas que optamos por escolas particulares de preço médio. Em qualquer caso, o resultado final dependerá da criança e não o receberemos antes de setembro.



Ou faz sentido levar o desenvolvimento iniciado aqui à sua conclusão lógica e organizar sua própria escola? O que você acha? Existem pessoas com ideias semelhantes com conhecimentos especializados e experiência no campo da educação?



Links úteis:

Russian Electronic School

Moscow Electronic School

Biblioteca MES para

desenvolvedores



All Articles