À propos des élèves de première année, de l'enseignement à distance et de la programmation asynchrone

image



Je suis un downshifter. Il se trouve que depuis trois ans, ma femme et moi apprécions le paysage rural à l'extérieur de la fenêtre, l'air frais et le chant des oiseaux. Les commodités dans la maison, l'Internet optique d'un fournisseur local, une puissante alimentation sans coupure et un covid qui est soudainement apparu, ont soudainement rendu l'idée de quitter une métropole pas si étrange.



Alors que j'étais engagé avec enthousiasme dans le développement Web, quelque part en arrière-plan, ma femme se plaignait périodiquement des problèmes de choix d'une école pour mon enfant. Et puis (soudainement) l'enfant a grandi et la question de l'école s'est levée. D'accord, alors le moment est venu. Voyons ensemble, qu'est-ce qui ne va toujours pas avec le système éducatif dans l'ancien 1/6 du pays, et que pouvons-nous y faire?



Je laisserai les méthodes traditionnelles d'enseignement en face à face en dehors de la portée de cet article. Je dirai seulement que les écoles ordinaires ont à la fois des avantages indiscutables et de graves inconvénients auxquels, d'ailleurs, l'auto-isolement forcé s'est récemment ajouté. Nous examinons ici les options d'éducation à distance et d'éducation familiale qui, pour diverses raisons, ont récemment attiré de plus en plus de parents.



Pour être clair: l'apprentissage à distance implique des cours dans une école ordinaire utilisant des «technologies d'apprentissage à distance» (DOT), et l'éducation familiale signifie l'abandon volontaire de l'école et l'apprentissage uniquement par la famille (en fait, c'est un bon vieux externat). Cependant, dans tous les cas, l'enfant doit être affecté à l'une des écoles disponibles, au moins pour la réussite des attestations intermédiaires.



Et maintenant quelques observations de la vie. Avec le transfert forcé à l'enseignement à distance d'enfants qui ont déjà étudié dans une école ordinaire, tout est triste. Les écoliers perçoivent ce don du destin comme une sorte de vacances, les parents ne sont pas habitués à suivre la discipline pendant les cours et, par conséquent, le rendement scolaire global diminue inévitablement.



Avec les élèves de première année, surtout dans le cas de la forme familiale, les parents ont peut-être une chance de mettre l'enfant «sur les rails» en utilisant l'intérêt naturel et l'effet de la nouveauté. Pour moi personnellement, atteindre l'indépendance est la tâche principale. Assis et faire ses devoirs avec un enfant, je considère le comble de la stupiditépas tout à fait raisonnable. Bien sûr, si vous voulez que vos enfants réussissent quelque chose dans la vie et ne restent pas autour de votre cou. Je veux donc que mon objectif soit d'apprendre à un enfant à apprendre, à poser correctement des questions et, en général, à penser avec sa propre tête.



Arriver au point. Choisir une école publique



Peut-être que j'aime davantage l'éducation familiale en raison de la possibilité de choisir un programme et un horaire de formation. Et vous pouvez physiquement aller moins souvent à l'école. Mais il faut choisir une école publique, parler au directeur du placement de l'enfant et recevoir un ordre d'inscription en première année à la fin de l'hiver pour qu'il n'y ait pas de surprises en septembre. Si, d'un point de vue juridique, la loi sur l'éducation ne semble pas exiger d'attestations annuelles, les «délais», d'après mon expérience, sont d'excellents facteurs de motivation, qu'il y ait donc des attestations. Il est peu probable qu’une école nous accepte à bras ouverts, mais je suis sûr que nous pourrons trouver une option valable dans la ville la plus proche.



Choisir un programme



Nous choisissons exactement. Essayer de composer un programme par vous-même sans avoir une formation spécialisée n'est pas raisonnable. Bien qu'il existe des ressources éducatives gouvernementales telles que l'école électronique russe ( NES ) et l'école électronique de Moscou ( MES ), ce qui en théorie serait suffisant, mais ... Les deux options fournissent des plans de cours, des vidéos, des tests et des tutoriels. Ce que je n'ai pas trouvé, ce sont les manuels eux-mêmes, même pour le programme obligatoire.



Et ici, le plus important manque: la communication. Enseigner à un enfant en lui montrant des vidéos sans fin et en le forçant à cocher des tests ne fonctionnera pas. Vous devez donc soit diriger les leçons de manière totalement indépendante, soit choisir l'une des écoles en ligne.



Choisir une école en ligne



Nous sommes presque revenus à notre point de départ. Télécommande? D'accord, regardons-la de plus près. Comment pouvez-vous organiser le processus éducatif à distance? Cela soulève de nombreuses questions, je ne soulèverai que les principales:



* Communication en direct. Que proposent les écoles? Skype, Tims au mieux. Des cours Skype? Vraiment? Si je ne me trompe pas, c'est 2020. Ouvrir plusieurs fenêtres avec de beaux boutons multicolores devant la première niveleuse et attendre qu'il ne les appuie pas, mais écoutera docilement un oncle ou une tante ennuyeux pendant une demi-journée? Je n'ai jamais vu de tels enfants. Et toi?



* Devoirs. Plus précisément, comment parvient-il à l'enseignant pour les tests? En fait, c'est une question vraiment difficile, peut-être même pas résolue en principe. Options existantes:



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

  2. . , - .

  3. . , .

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

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



* Estimations. De toute évidence, les notes données dans la leçon et lors de la vérification des devoirs doivent figurer dans un journal électronique mis à la disposition des parents. Et ils y arrivent. Mais pas tout de suite. J'ai demandé à des enfants plus âgés diplômés de l'un des prestigieux lycées avec un dôme en or (ironiquement, avec un biais informationnel), pourquoi? La réponse, pour être honnête, m'a surpris. Il s'avère que les enseignants notent les notes sur une feuille de papier et, après les leçons, les conduisent dans ce journal très électronique sur le portail de l'État. Et cela pendant que Tesla Elon Musk laboure l'immensité de l'espace ...



D'accord, il est temps de faire un peu de recherche technique et de vérifier s'il y a des raisons objectives à cet état de fait?



Définissons les exigences d'une plate-forme d'apprentissage idéale hypothétique. En fait, tout est simple: les enfants doivent rester dans la leçon, se concentrer sur ce que dit et montrer l'enseignant, répondre aux questions si nécessaire et lever la main si on le souhaite. Fondamentalement, nous voulons une fenêtre plein écran avec un flux de la caméra, de la présentation ou du tableau blanc de l'enseignant. Le moyen le plus simple d'y parvenir est d'utiliser la technologie WebRTC(communications en temps réel, communication en temps réel). Cette chose fonctionne dans n'importe quel navigateur plus ou moins moderne, ne nécessite pas l'achat d'équipement supplémentaire et, de plus, fournit une connexion de bonne qualité. Et oui, ce standard nécessite au moins une programmation asynchrone car la méthode JS requise navigator.mediaDevices.getUserMedia () renvoie une promesse . Tout semble clair, je commence à le mettre en œuvre.



Digression lyrique sur le choix d'un cadre
, «» 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 est conçu pour une communication directe entre les navigateurs, en utilisant la technologie point à point (p2p). Cependant, pour établir cette connexion, les navigateurs doivent s'informer mutuellement de leur intention de communiquer. Cela nécessite un serveur d' alarme .



Un exemple d'implémentation de base d'un chat vidéo simple utilisant la topologie "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();
})();

      
      







Le serveur de signalisation est fait sur le framework Python aiohttp et est une simple "vue" qui fait un proxy des requêtes WebRTC. La connexion au serveur dans cet exemple se fait via des sockets Web . De plus, de simples données de chat textuel sont transmises via le canal de signalisation.



Exemple d'implémentation du serveur de signalisation
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

      
      







La technologie WebRTC, en plus de la communication vidéo, vous permet d'autoriser le navigateur à capturer le contenu d'un écran ou d'une application distincte, ce qui peut être indispensable lors de la conduite de cours en ligne, de webinaires ou de présentations. Génial, utilisons-le.



J'étais tellement emporté par les possibilités modernes de la communication vidéo que j'ai presque oublié le sujet le plus important de la classe - le tableau blanc interactif. Cependant, l'implémentation de base est si simple que je ne surchargerai pas cet article avec elle. Nous ajoutons simplement le canevas , écoutons les événements de déplacement de la souris onmousemove (ontouchmove pour les tablettes) et envoyons les coordonnées résultantes à tous les points connectés via le même serveur de signalisation.



Tester votre tableau blanc interactif



Ici, vous avez besoin d'une tablette, d'un numériseur et d'un enfant vivant. Dans le même temps, nous vérifierons la possibilité de numériser la saisie manuscrite.



Pour commencer, j'ai pris une vieille tablette Galaxy Tab sous Android 4.4, un stylet fait maison et les premières recettes que j'ai rencontrées comme arrière-plan de la toile. Je n'ai pas installé de programmes supplémentaires. Le résultat m'a découragé: ma tablette n'est absolument pas adaptée à l'écriture! Autrement dit, il n'y a pas de problème à déplacer votre doigt dessus, mais placer le stylet dans le contour d'une lettre, même aussi énorme que sur l'image ci-dessous, est déjà un problème. De plus, le gadget commence à s'émousser en cours de dessin, ce qui entraîne la rupture des lignes. De plus, je ne pouvais pas faire en sorte que l'enfant ne repose pas son poignet sur l'écran, ce qui laisse une trace supplémentaire à portée de main, et la tablette elle-même commence à ralentir encore plus. Conclusion: une tablette ordinaire pour écrire sur un tableau blanc ne convient pas. Le maximum de ses capacités est de déplacer votre doigt sur l'écran de chiffres plutôt grands. Mais il est trop tard pour offrir cela aux écoliers.



D'accord, c'est de la recherche purement théorique, non? Ensuite on prend un numériseur (aka tablette graphique) au format Wacom Bamboo A8, et on regarde l'enfant.



Notez que mon sujet de test de six ans a reçu un ordinateur portable avec un stylo graphique pour la première fois de sa vie. Il nous a fallu dix minutes pour acquérir les compétences de base pour utiliser le stylo, et déjà dans la deuxième leçon, l'enfant a utilisé la tablette avec assez de confiance, effacé indépendamment du tableau, dessiné des visages, des fleurs, notre chien et a même commencé à pousser des boutons disponibles dans epsilon, posant simultanément des questions comme "Pourquoi lèvent-ils la main à l'école?" Mais le résultat laissait invariablement à désirer. Le fait est que les designers et les artistes maximisent le fragment de l'image pour dessiner un élément, ce qui rend les lignes précises. Ici, nous devrions voir le tableau entier, sur une échelle de 1: 1. Ici et l'adulte ne tombera pas dans la ligne. Voici ce que nous avons:



image



Verdict final: aucune écriture n'est hors de question. Et si nous voulons «mettre la main» à nos enfants, nous devons y parvenir par nous-mêmes, sur le papier, l'école n'y aidera pas.



Je dois dire que l'enfant a pris toutes mes expériences avec enthousiasme et, de plus, depuis, me suit avec une queue et me demande de «mettre les recettes». Déjà bien, la compétence acquise lui sera utile, uniquement à des fins complètement différentes.



Quoi qu'il en soit, à la suite d'expériences, j'ai en fait obtenu un MVP - un produit minimum viable, presqueconvient aux leçons en ligne, avec vidéoconférence / audio, écran partagé, tableau blanc interactif, chat textuel simple et bouton lever la main. C'est au cas où l'enfant n'aurait soudainement pas de microphone. Oui, cela se produit, surtout chez les enfants qui n'ont pas appris leurs leçons.



Mais dans ce tonneau de miel, malheureusement, il y a quelques cuillères à goudron.



Test du WebRTC



Cuillère numéro 1. Puisque notre communication vidéo utilise des connexions directes entre clients, la première étape consiste à tester l'évolutivité d'une telle solution. Pour le test, j'ai pris un vieil ordinateur portable avec un i5-3230M bicœur à bord, et j'ai commencé à y connecter des clients avec des caméras Web désactivées, c'est-à-dire en émulant un mode un-à-plusieurs:



image



comme vous pouvez le voir, l'ordinateur portable expérimental est capable de diffuser plus ou moins confortablement vers cinq clients ( à une charge CPU de 60%). Et ceci à condition que la résolution du flux vidéo sortant soit réduite à 720p (640x480px) et la fréquence d'images à 15 ips. En principe, pas si mal, mais lors de la connexion d'une classe de plusieurs dizaines d'étudiants, vous devrez abandonner le "maillage complet" au profit de la cascade, c'est-à-dire que chacun des cinq premiers clients proxie le flux aux cinq suivants, et ainsi de suite.



Cuillère numéro 2.Pour créer une connexion interactive directe (ICE) entre les clients, ils doivent contourner les pare-feu et les NAT. Pour ce faire, WebRTC utilise un serveur STUN , qui informe les clients des paramètres de connexion externes. On pense que dans la plupart des cas, cela suffit. Mais j'ai été presque immédiatement "chanceux":



Comme vous pouvez le constater, le débogueur se plaint de l'impossibilité de la connexion ICE et nécessite la connexion d'un serveur TURN, c'est-à-dire un relais. Et c'est déjà CHER. Un simple serveur de signalisation est indispensable. Conclusion - vous devez faire passer tous les flux via le serveur multimédia.



Serveur multimédia



Pour les tests, j'ai utilisé aiortc . Une évolution intéressante, permet de connecter le navigateur directement au serveur via WebRTC. Une signalisation séparée n'est pas nécessaire, vous pouvez utiliser le canal de données de la connexion elle-même. Cela fonctionne, tous mes points de test connectés sans problème. Mais le problème des performances. Un simple écho d'un flux vidéo / audio avec les mêmes limites de 720p et 15fps a mangé 50% de mon CPU virtuel sur le VDS de test. De plus, si la charge est augmentée à 100%, le flux vidéo n'a pas le temps d'être déchargé vers les clients et commence à obstruer la mémoire, ce qui conduit finalement à un arrêt du serveur. De toute évidence, le Python que nous aimons utiliser pour le traitement des E / S n'est pas très lié au processeur. Nous devrons chercher une solution plus spécialisée, par exemple, Janusou Jitsy .



Dans tous les cas, une solution complète nécessitera des serveurs dédiés, selon mes estimations, à raison de 1 core par salle (classe). Cela coûte déjà de l'argent et va au-delà du simple test, donc à ce stade, je considérerai la première phase de ma recherche terminée.



conclusions



1. Pour le moins dire, il est étrange de voir des instructions de téléchargement et des liens pour vous inscrire au programme de l'ancien ennemi potentiel numéro 1 (ici à propos de Microsoft Teams) sur le portail officiel de la Fédération de Russie . Et c'est à l'ère des sanctions et de la substitution des importations.

Non, personnellement je suis pour l'amitié des peuples et, en général, pour toutes sortes de tolérances, mais est-ce vraiment seulement mes cheveux qui se détachent de cette «intégration»? N'y a-t-il pas nos développements?



2. Intégration du MES / NES avec les écoles. En fait, les développeurs MES sont super, ils ont même fait l'intégration avec Yandex.tutor. Qu'en est-il de la notation en temps réel pendant les cours, quand y aura-t-il une API? Ou suis-je pas au courant de quelque chose?



3. Lorsque vous choisissez une forme d'éducation à distance ou familiale, vous devez être honnête avec vous-même: vous ne pourrez pas transférer la responsabilité de l'éducation de votre enfant à l'école. Tout le travail de conduite des cours (dans le cas de l'éducation familiale), de maintien de la discipline et d'auto-organisation (en tout cas) incombe entièrement aux parents. Vous devez en être conscient et trouver du temps pour les cours. Cependant, dans les familles nombreuses, cela ne devrait pas être un problème.



4. Je n'inclurai pas de liens vers des écoles en ligne sélectionnées ici, de peur que cela ne soit considéré comme une publicité. Je dirai seulement que nous avons choisi des écoles privées de moyenne gamme. Dans tous les cas, le résultat final dépendra de l'enfant et nous le recevrons au plus tôt en septembre.



Ou est-il judicieux d'amener le développement commencé ici à sa conclusion logique et d'organiser votre propre école? Qu'est-ce que tu penses? Existe-t-il des personnes partageant les mêmes idées et possédant des connaissances et une expérience spécialisées dans le domaine de l'éducation?



Liens utiles: Bibliothèque MES de l' école électronique

russe de

Moscou

pour les

développeurs



All Articles