GithubHelp home page GithubHelp logo

readthedocs-fr / bin-server Goto Github PK

View Code? Open in Web Editor NEW
15.0 5.0 15.0 192 KB

Un outil pour héberger des snippets de code et les partager via une URL.

Home Page: https://bin.readthedocs.fr

License: MIT License

Python 66.58% HTML 9.76% CSS 14.08% JavaScript 8.38% Shell 0.66% Dockerfile 0.54%
hacktoberfest

bin-server's Introduction

PyPI GitHub Workflow Status Discord Documentation License

bin-server

Un outil pour héberger des snippets de code et les partager via une URL.

Installation

Le service nécessite une version de Python supérieure ou égale à Python 3.7 et un accès à un serveur Redis. Les archives sont hébergés sur Pypi, vous pouvez installer la dernière version stable via pip :

$ pip install rtd-bin-server

Une fois installé, le module bin devient accessible et peut être directement lancé via la ligne de commande.

$ python -m bin

Par défaut, le service ne traite qu'un client à la fois, écoute à l'adresse localhost:8012 et se connecte à la base de donnée Redis sur localhost:6379/0. Changer la configuration par défaut se fait au moyen d'un fichier dotenv. Le fichier sera automatiquement détecté s'il est nommé .env et qu'il se trouve soit au niveau des sources, soit au niveau du répertoire courant (celui depuis lequel est lancé la commande). Une alternative est de renseigner le fichier de configuration à utiliser via l'option --rtdbin-config.

$ python -m bin --rtdbin-config /chemin/vers/fichier/.env

La configuration complète par défaut est reprise ci-dessous :

RTDBIN_HOST=localhost
RTDBIN_PORT=8012
RTDBIN_MAXSIZE=16kiB
RTDBIN_DEFAULT_LANGUAGE=text
RTDBIN_DEFAULT_MAXUSAGE=0
RTDBIN_DEFAULT_LIFETIME=0
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
REDIS_PASSWORD=
REDIS_USERNAME=

Par défaut, le service utilise le serveur web wsgiref disponible dans la bibliothèque standard de Python pour traiter les requêtes. Ce serveur est propice dans un environnement de développement ou lorsque le volume d'utilisateur est réduit. Pour de meilleures performances, un serveur tiers compatible wsgi peut être utilisé à la place.

$ pip install gunicorn
$ gunicorn bin:app

Des fichiers de configuration d'exemples pour nginx, systemd et gunicorn sont disponibles dans le wiki.

Contribution

Le développement de bin se fait principalement via la communauté Discord Read The Docs dans le canal #bin.

Les sources peuvent être récupérées via Git et le service peut être installé dans un environnement virtuel dédié.

$ git clone https://github.com/readthedocs-fr/bin.git rtdbin
$ cd rtdbin
$ python -m venv
$ venv/bin/pip install -r requirements.txt
$ venv/bin/pip install -e .

Une fois installé, vous pouvez vous assurer que le système est correctement opérationnel en lançant la suite de tests unitaires et en vérifiant que le serveur démarre correctement.

$ venv/bin/python -m unittest
$ venv/bin/python -m bin &
$ curl http://localhost:8012/health
$ kill %%

Les contributions se font sur des branches dédiées, les branches sont nommées en commençant par quelques mots-clés suivis d'un identifiant de l'utilisateur. Les commits sont le plus petit dénominateur de version, sont correctement documentés au moyen d'un message de commit reprenant au minimum un contexte expliquant la nécessité des modifications.

$ git checkout main
$ git pull origin main
$ git checkout -b redo-readme-juc
$ git commit <<EOF
doc: redo readme

The readme is the first document users read when they learn
about the project, the file is by default shown in the project root
page in github.

The document is very important yet the current one is not very
good. The new documents are more straight to the point.
EOF
$ git push fork redo-readme-juc

bin-server's People

Contributors

aakodal avatar acaretia avatar antoinejt avatar fusetim avatar github-actions[bot] avatar julien00859 avatar lahgolz avatar linekio avatar manuel12 avatar mesteery avatar nauhai avatar pilna avatar quentinbubu avatar wyx0-xyz avatar zazbone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

bin-server's Issues

Word wrap

Lorsqu'on ouvre un bon sur téléphone (un fichier txt) les phrases sont parfois très longues et nécessitent de scroll vers la droite pour afficher tout le contenu. Pour des fichiers txt le wordwrap peut être intéressant. L'inverse est également pour du code source, il est plus facile de lire du code sans le wordwrap.

Je propose d'ajouter une option pour pouvoir activer le wordwrap à la demande. On pourrait avoir une réflexion sur comment faire en sorte que l'option soit accessible aux utilisateurs.

Un exemple me vient en tête, sur GitHub juste une diff, il y a moyen d'ajouter ?w=1 à l'URL pour faire en sorte que les changements d'indentations soient pas repris dans la diff. On n'a pas forcément besoin d'ajouter des boutons de tous les côtés quoi

Implémentation de la persistance via Redis

Nous avons décider d'utiliser une base de données Redis. Le snippet et les meta-data seront sauvegardés dans un HASH ; la clé sera l'identifiant unique du snippet.

Structure du Hash envisagée (représentation sous forme de dictionnaire) :

{
    "<key>": {
        "parent": "<parent key>",
        "snippet": "<code>",
        "extension": "<snippet language>"
    }
}

(Editez pour corriger la structure, j'ai oublié ce qu'on avait dit exactement)

Voir Issue #10 : Système de versionning (enfant -> parent).

Meilleure intégration discord du lien

Pour le moment, lorsque discord vient un lien rtdbin, il affiche une petite intégration de la sorte:

image

Je propose qu'on retravaille cette intégration pour par exemple remplacer "A simple pastebin" par " snippet" dans la page hightlight.html. Peut-être intégrer d'autres modifications comme afficher les 2/3 premières lignes du bin ?

Support du Drag&Drop

L'ajout du drag&drop de fichier sur l'interface Web semble une feature à envisager.
Cela demandera sûrement quelques efforts sur le front.

Possibilité de supprimer son bin.

Une feature intéressante serait que, au moment du post d'un nouveau bin, l'auteur reçoive un code qu'il peut mettre de côté afin de pouvoir supprimer à tout moment le contenue du bin.

Packaging Pypi

Pour le moment je fais la packaging sur Pypi manuellement, j'aimerais automatiser le processus via l'intégration continue. Juste avant qu'une PR est mergée, il faudrait recommiter pour changer le fichier VERSION en fonction du titre du commit ou (prioritaire) d'un tag présent sur la PR.

Pour un commit fix on incrémente BUILD, pour les autres commits on incrémente MINOR. Si le tag "Breaking change" (qui est à créé) existe sur la PR on doit incrémenter MAJOR.

Pour le moment le package est sauvegardé via mon propre compte, je n'ai pas tellement de problème à ce qu'on le réhéberge avec un compte "officiel" readthedocs. Pour le moment la commande que j'utilise pour uploader un package sur pypi est la suivante:

$ python3 -m twine upload dist/rtd_bin_server-1.0.0-py3-none-any.whl

Et twine me prompt pour mon username/password. Il faudrait investiguer pour ne pas être prompt voire mieux utiliser une clé d'api.

Rajouter une utilisation max

Car la redirection à la création d'un bin consomme une utilisation, ca serait bien de rajouter une utilisation quand l'utilisateur en a spécifié une pour que le nombre choisi soit plus cohérent.

Système de versionning (enfant -> parent)

Il sera possible sur chaque snippet de faire une "révision" de celui-ci, c'est à dire une version modifiée/corrigée de celui-ci. Chaque snippet sera donc relié à son parent via un lien cliquable sur l'interface.

Les limitations de Redis font qu'il sera difficile d'établir une relation entre un snippet et ses révisions (lister les différentes révisions du snippet), et nous avons donc décidé dans un premier temps de ne pas implémenter cette fonctionnalité.

À faire :

  • Back : ajouter l'id du parent dans le hash du snippet dans la bdd
  • Front : ajouter un lien cliquable/bouton qui ferait une requête GET sur /<parent id>
  • Front : ajouter un bouton pour créer une nouvelle révision d'un snippet existant

Weird encoding in curl/multipart

PS C:\Users\me> curl http://localhost:8012/new --form "code=èèèéééèéóò^yô" -v
*   Trying ::1...
* TCP_NODELAY set
*   Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8012 (#0)
> POST /new HTTP/1.1
> Host: localhost:8012
> User-Agent: curl/7.55.1
> Accept: */*
> Content-Length: 152
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=------------------------ae1f37c45e1af816
>
* Done waiting for 100-continue
* HTTP 1.0, assume close after body
< HTTP/1.0 400 Bad Request
< Date: Sun, 14 Feb 2021 13:46:09 GMT
< Server: WSGIServer/0.2 CPython/3.9.1
< Content-Length: 800
< Content-Type: text/html; charset=UTF-8
<
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html>
        <head>
            <title>Error: 400 Bad Request</title>
            <style type="text/css">
              html {background-color: #eee; font-family: sans;}
              body {background-color: #fff; border: 1px solid #ddd;
                    padding: 15px; margin: 15px;}
              pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
            </style>
        </head>
        <body>
            <h1>Error: 400 Bad Request</h1>
            <p>Sorry, the requested URL <tt>'http://localhost:8012/new'</tt>
               caused an error:</p>
            <pre>'latin-1' codec can't encode characters in position 0-9: ordinal not in range(256)</pre>        </body>
    </html>
* Closing connection 0

Originally posted by @Mesteery in #102 (comment)

Support de langages supplémentaires

Il faudrait supporter davantage de langages sans pour autant les afficher dans le front.
Ce serai utile d'avoir une liste un peu plus exhaustive de langages supportés (pas forcément affichés dans select), cela permettrait par exemple aux applications tierces de pouvoir mettre du code d'un peu près n'importe quel langage (e.g. bin-vscode uploadant un PowerShell Session). Cela permettrait aussi aux utilisateurs de pouvoir utiliser plus de langage.

Documentation

On arrive doucement à la v1, il faudrait docstringuer les méthodes et ajouter une doc sphinx.

Intégration continue

Dépend de #44

Afin d'assurer que chaque contribution externe ne casse pas le projet, je propose d'ajouter un mécanisme d'intégration continue via une quelconque technologie (github actions, travis, jenkins, ...) afin de faire passer la batterie de tests unitaires pour chaque nouvelle pull-request.

Vu qu'incorporer un tel outil vient changer le process de développement, un effort devra être fait au niveau soit de la documentation soit de la formation à l'outil.

Tabs support

Ce serait une feature intéressante que de supporter les tabulations dans le code.
Actuellement, il est possible d'indenter proprement son code sans le copier-coller ou utilisez la barre espace.

A propos de ça, il pourrait également être intéressant de pouvoir modifier la taille d'une tabulation d'indentation en bas de l'écran.

Résumé :

  • Supporter les tabulations
  • Permettre de modifier la taille d'une tabulation d'indentation

Le cas KaYoXx

Je fais suite à un incident qu'il y a eu ce dimanche 21 février, un utilisateur a hébergé un malware sur notre service de bin. Des incidents de ce genre sont possibles mais nous devrions prendre des mesures pour pouvoir les atténuer.

Pour cette fois-ci, l'incident m'a été rapporté sur discord et j'ai pu manuellement supprimer le snippet problématique (en plus que de lister le gars dans ma naughly list, il est banni à vie).

Pour limiter ce genre de problème, je pense que nous devrions ajouter du logging "[INFO] just created a snippet : " ainsi qu'un bouton "report" qui enverrait un message au niveau WARNING dans les logs et qui sauvegarderait le message d'incident sur disque. Avec fail2ban ce sera facile de m'envoyer un email quand ce genre d'incident se produit.

Tests unitaires

Il faut mettre en place une stratégie de tests unitaire et l'incorporer à de l'intégration continue.

Il existe deux bibliothèques pour les tests unitaires en python: unittest dans la bibliothèque standard et pytest comme module externe. Chacune a ses avantages et inconvéniants.

L'ensemble de ces tests doivent être écrit:

  • Tester bin/utils.py ne devrait pas poser de problème
  • Tester bin/models.py demande d'accéder à une instance redis, en local pas de soucis, pour le CI on peut fournir une config qui fait que ça se connecterait au redis chez moi, il faudra s'assurer que le pwd soit caché
  • Tester bin/controller.py va demander de jouer avec du http, on peut ajouter requests dans les dépendances pour les tests unitaire ou bien (si qqun est assez fou pour) on peut utliser urllib. Le mieux serait de vérifier les réponses HTTP, même celle pour le code highlight.

Implémentation de la coloration syntaxique (highlight.js)

Les snippets seront colorés d'une manière élégante pour améliorer leur lisibilité. Cette coloration syntaxique se fera grâce à la librairie highlight.js et en fonction du langage choisi par l'utilisateur lors de la création du snippet.

Ancre pour lier une ligne précise dans un snippet

Les utilisateurs auront la possibilité de marquer/ancrer une ligne dans un snippet et cela ajoutera le numéro de la ligne dans l'URL. Ainsi, lorsque d'autres utilisateurs se rendront sur le lien, la ligne sera surlignée et directement au centre de la page (si le snippet a une taille supérieur à la taille de la page), comme c'est possible sur github. Cela permet de partager plus facilement des lignes précises dans les snippets.

Voir Issue #7 : Numérotation des lignes.

Numérotation des lignes

Chaque ligne des snippets seront numérotées afin de pouvoir plus facilement indiquer les lignes importantes/posant problème lors des partages de ces snippets.

RFC 03 - Configuration

RFC 03 - Configuration

Ce document est une RFC (Request for Comments), toute critique est la
bienvenue à partir du moment où elle se veut constructive.

Statut du document : Proposition
Dernière révision le : 30 octobre 2020 à 20 h 39
Auteurs : FuseTim

Résumé

Nous énumérerons les différents moyens de mettre en place la configuration de
notre projet rtd-bin, service de pastebin de la communauté Read The Docs.

Contexte

Le service de rtd-bin doit communiquer avec plusieurs services, notamment le
service Redis qui sert de base de données et à un service de reverse-proxy afin
d'écouter les requêtes des différents utilisateurs du service.

Pour permettre la communication entre ces différents services, des couples
d'adresse IP-port et des identifiants de connexion doivent être renseignés. De
fait, ces informations doivent être renseignées par l'administrateur du système
et doivent donc être facilement modifiable et éviter d'être compromis.

Également, peut-être soumis à une configuration la liste des langages et les
extensions de fichiers associées du bin. Bien que peu souvent modifiée, il
reste important de permettre à l'administrateur du service de le modifier
aisément.

Divers moyens de configuration

Dans le monde, des programmes informatiques, il existe 3 grands moyens de
permettre la configuration d'un logiciel. Il semble approprié de les citer, et d'y
chercher leurs inconvénients et avantages.

Arguments en ligne de commande

Exemple :

$ python -m bin <arg1> [--opt1=value1]

Pour un nombre d'arguments relativement faible, l'utilisation d'arguments et
d'options en ligne de commande permet de facilement utiliser et configurer un
logiciel. Également, l'implémentation est simple si les arguments reste
raisonnable en nombre.

Toutefois l'utilisation des arguments en ligne de commande peut se révéler
fastidieuse lorsque les arguments sont multiples et également plus difficile à
automatiser. Également, l'utilisation de secret en argument est à éviter puisque
ceci sont visibles dans l'ensemble du système (ainsi que les processus enfants),
ce qui cause des risques de compromission.

Variables d'environnement

Exemple :

$ KEY1=VALUE1 python -m bin

Les variables d'environnement est un moyen efficace de configurer un logiciel,
toutefois certaines limitations existent, notamment la compatibilité avec
Windows est plus difficile à réaliser puisqu'il n'existe que les variables
globales et les variables utilisateurs. De plus, l'utilisation d'objets, liste ou
map n'y 'est pas naturelle.

La compromission des variables d'environnement est moindre mais il faut noter
toutefois qu'elles se propagent aux processus enfants. Il est nécessaire d'y
faire particulièrement attention.

Fichiers de configuration

Exemple :

listen = "localhost"
port = 8080
compress = true

[redis]
host = "localhost"
port = 6379
user = "my_user"
password = "..."
prefix = "rtd.bin"

#...

Le fichier de configuration est moyen particulièrement adapté à de nombreuses
variables et à de nombreux types de valeur (objets, listes, map, date, etc.).
Également, rien n'empêche la séparation des variables en plusieurs fichiers de
configuration, particulièrement utile pour de grande map clé-valeur.
Aussi, il reste convenable de les utiliser pour des secrets si leurs permissions d'accès
sont bien gérées.

Cependant l'utilisation de fichier de configuration oblige l'utilisation du
système de fichier, ce qui peut être embêtant lors de l'usage avec certains
environnements Docker. Également, il nécessite une nouvelle dépendance afin de
parcourir la configuration. Toutefois beaucoup de formats et d'implémentations
existent, ce qui offre une grande diversité dans le choix.

Implémentation

L'implémentation choisie doit être discutée avec l'ensemble des
contributeurs potentiels. L'implémentation est cependant nécessaire pour
permettre la continuité du développement du projet rtd-bin.

Ajouter un guide à la page d'acceuil

Hello !

Un petit manuel d'utilisation sur la page par défaut du bin permettrait d'éviter d'expliquer aux nouveaux comment l'utiliser

Quand on redirige les nouveaux sur le bin pour les codes longs, il faut souvent leur expliquer le fonctionnement du bin
Le manuel n'aurai pas besoin d'être long. Simplement un peu plus long que le "Entrez votre code" actuel

Exemple:

Copier coller votre code ici
Cliquez sur le bouton bleu "enregistré" en bas à droite
Et partager l'adresse de la page

Merci ❤️

Problem: possible XSS when accessing raw html snippets

Reproduce XSS attack

  1. Create a snippet containing the following code (the selected language in the dropdown list doesn't matter):
<html>
<body>
<script>
alert("XSS");
</script>
</body>
</html>

The generated url should look like https://rtdbin.fusetim.tk/upfila.html

  1. Add /raw right after the root: https://rtdbin.fusetim.tk/raw/upfila.html
  2. Navigate to the new url: you will notice the javascript code is executed
    image

Anyone that clicks on the raw link will be subject to an XSS attack; while it doesn't matter right now, this could provide attackers with a way to steal sessions or other client side stored data in the future.

Root of the problem

In controller.py, in the /raw route, the returned response doesn't contain a Content-Type header telling the client browser to interpret data as plaintext.

Proposed fix

Force Content-Type header to the following text/plain; charset=utf-8. This should prevent the client browser from blindly interpreting the snippet as a legit html page.

Longévité des snippets

Au lieu de limiter les snippets dans le temps, nous avons décider de limiter la taille de la base de donnée Redis. Ainsi, lorsque l'usage mémoire sera trop conséquent (valeur à déterminer), les snippets peu lus et plus anciens seront supprimés pour laisser place aux nouveaux. Cela sera réalisable grâce au mécanisme LRU de Redis.

Les snippets pourront tout de même avoir une limite de temps choisie par l'utilisateur, qui sera définie par défaut sur 1 semaine, et aussi une limite d'utilisations qui sera par défaut infinie.

À faire :

  • Limite d'utilisation des snippets
  • Limite de temps de vie des snippets
  • Limite de la taille de la base de données (À faire localement sur la bdd)

Problème : Les requêtes GET qui retournent des templates sont doublées

J'ai pu remarquer en faisant le système de longévité des snippets (en fonction du nombre d'utilisations) que les requêtes GET renvoyant des templates sont doublées.

Comment reproduire le problème :

  • Lancer le serveur à l'aide de la commande .\venv\Scripts\python -m bin sur Windows ou /venv/bin/python -m bin sur Linux (en considérant que vous êtes à la racine du projet et que vous avez installé le serveur avec la commande .\venv\Scripts\pip install -e . sur Windows ou /venv/bin/pip install -e . sur Linux).
  • Se connecter sur l'adresse localhost:8080.
  • On peut déjà voir dans le terminal que la requête GET sur / qui renvoie la template de la création d'un nouveau snippet est appelée 2 fois :
127.0.0.1 - - [30/Oct/2020 20:58:18] "GET / HTTP/1.1" 200 661                             <---
127.0.0.1 - - [30/Oct/2020 20:58:18] "GET /assets/css/style.css HTTP/1.1" 304 0
127.0.0.1 - - [30/Oct/2020 20:58:18] "GET / HTTP/1.1" 200 661                             <---
127.0.0.1 - - [30/Oct/2020 20:58:19] "GET /favicon.ico HTTP/1.1" 404 724
  • On peut aussi noter que les requêtes GET sur les assets ne sont elles appelées qu'une fois. Je pense que c'est donc propre aux templates.
  • Créer un nouveau snippet sur le site et l'envoyer
  • On peut voir que la requête GET sur /<id> qui fournit la template du snippet est elle aussi appelée 2 fois :
127.0.0.1 - - [30/Oct/2020 20:58:34] "POST /new HTTP/1.1" 303 0
127.0.0.1 - - [30/Oct/2020 20:58:34] "GET /raheve HTTP/1.1" 200 641          <--
127.0.0.1 - - [30/Oct/2020 20:58:34] "GET /raheve HTTP/1.1" 200 641          <--

Je sais pas trop d'où est-ce que ça peut venir c'est pour ça que je fais une issue et que je règle pas moi même le problème.

Héberger une instance redis de testing

Nous avons eu des retours comme quoi certaines personnes voudraient bien contribuer au développement de bin mais qu'ils étaient ralenti par la nécessité d'héberger un serveur redis de leur côté pour les tests. C'est notamment un problème pour les développeurs sous Windows puisque redis ne propose aucun binaire pour ce système.

Que pensez-vous d'héberger une instance redis pour la communauté des développeurs. Redis propose des ACL uniquement depuis la version 6, hors le service est hébergé du une machine debian stable où le package est encore en version 5 (mais bonne nouvelle, debian 11 "Bullseyes" devrait passer en stable d'ici la fin de l'année). Le seul méchanisme de protection que je peux proposer est la configuration via un mot de passe unique qui serait partagé par toute la communauté, rendant très difficile le fait de cloturer un compte sans devoir changer le mot de passe et devoir le re-communiquer à tout le monde.

Une autre considération à avoir est que redis ne se veut pas être un système multi-tenant, à comprendre que la "database" auquel le développeur se connecte est en réalité juste un namespace au sain d'une même db redis, il n'y a aucune isolation entre les "database" (cf: redis/redis#8099 (comment)).

(Discord) Bloc de code -> bin

Cette fonctionnalité se fera à l'aide d'un bot Discord, nommé ReadTheBin.

Roadmap :

  • Gestion des blocs de code comportant au minimum 20 retours à la ligne (\n). Le message original est supprimé et un message identique est envoyé à l'aide d'un embed, remplaçant les blocs de code par les liens des bins.
  • Le message envoyé par le bot peut être supprimé par l'auteur du message originel, à l'aide d'une simple réaction.
  • Gestion des fichiers message.txt afin d'en récupérer les blocs de code et de les transformer en bin. Le fichier ne sera pas supprimé mais le bot essaiera tout de même de renvoyer son contenu en remplaçant les blocs de code par les liens des bins à l'aide d'un embed. Si la taille excède d'une manière ou d'une autre la limite imposée par Discord, le message ne sera constitué que des liens vers les bins.
  • Support des requêtes au service de bin
  • Liaison des requêtes à rtd-bin (en attente de la mise en place dudit service)

Révision du 29/10/2020 à 01h00 : Les webhooks seraient BEAUCOUP trop API-spammy et le bot se retrouverait rate-limit. Il en a été conclu que les embeds seront utilisés à la place.


Développeur assigné : @Aakodal

front: style

Je pense qu'on pourrait encore plus améliorer l'UI du front :

  • un meilleur thème pour le code
  • une meilleur font
  • (d'autres améliorations ?)

Pour le coup, c'est que mon avis personnelle, et peut-être que ca plait a la majorité des personnes.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.