# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['circe']

package_data = \
{'': ['*'], 'circe': ['static/*']}

install_requires = \
['aiofiles>=0.8.0,<0.9.0',
 'argh>=0.26.2,<0.27.0',
 'asyncio>=3.4.3,<4.0.0',
 'huey>=2.4.3,<3.0.0',
 'itsdangerous>=2.1.2,<3.0.0',
 'markdown2>=2.4.3,<3.0.0',
 'python-dotenv>=0.20.0,<0.21.0',
 'python-json-logger>=2.0.2,<3.0.0',
 'requests>=2.28.0,<3.0.0',
 'sanic>=22.3.2,<23.0.0']

entry_points = \
{'console_scripts': ['circe = circe.__main__:run_cli']}

setup_kwargs = {
    'name': 'circe-certic',
    'version': '0.0.39',
    'description': 'Circe Server',
    'long_description': '# Circe\n\nAPI web pour la transformation de documents.\n\n## Table des matières\n\n- [Description du service](#description-du-service)\n\t- [Format d\'échange](#format-déchange)\n\t\t- [Structure du fichier job.json](#structure-du-fichier-jobjson)\n\t- [Web API](#web-api)\n\t\t- [GET /transformations/](#get-transformations)\n\t\t- [POST /job/](#post-job)\n\t\t- [GET /job/[UUID]](#get-jobuuid)\n\t- [Notification](#notification)\n- [Serveur de référence](#serveur-de-référence)\n\t- [Pré-requis](#pré-requis)\n\t- [Installation et démarrage du service](#installation-et-démarrage-du-service)\n\t- [Variables d\'environnement et configuration par défaut:](#variables-denvironnement-et-configuration-par-défaut)\n\t- [Utilisation en ligne de commande](#utilisation-en-ligne-de-commande)\n\t- [Authentification](#Authentification)\n\t- [Ajouter des transformations](#ajouter-des-transformations)\n- [Clients de référence](#clients-de-référence)\n- [Tests](#tests)\n- [Mise en production](#mise-en-production)\n\n\n## Description du service\n\n### Format d\'échange\n\nLe client fournit au serveur une tâche (un _job_) à effectuer sous forme d\'une archive tar *gzippée* (*.tar.gz) \ncontenant a minima **à sa racine**:\n\n- les fichiers à transformer\n- un fichier nommé job.json décrivant les transformations souhaitées sur ces fichiers\n\nOn peut également ajouter des fichiers utiles à la conversion, tel que des feuilles de styles ou des fichiers de \nfontes par exemple.\n\n#### Structure du fichier job.json\n\nEst placé dans l\'archive un fichier ```job.json``` décrivant l\'ensemble des opérations à faire sur les fichiers.\n\nUn cas minimal:\n\n    {\n        "transformations": [\n            {"name": "html2pdf"}\n        ]\n    }\n\n... décrit une transformation unique à effectuer sur les documents fournis dans l\'archive, sans options.\n\nLa seule clef obligatoire pour le job est la clef ```transformations```, contenant la liste des transformations à faire.\nLa seule clef obligatoire pour la transformation est la clef ```name```, contenant le nom de la transformation.\n\nUn cas plus complet:\n\n    {\n        "transformations": [\n            {"name": "html2pdf",\n             "options": {"compression": 1}},\n            {"name": "donothing",}\n        ],\n        "notify_hook": "http://www.domain.tld/notify-me/"\n    }\n\n... décrit 2 transformations consécutives, dont une avec une option, ainsi qu\'une URL de notification \n(```notify_hook```) qui sera appelée par le serveur à la fin du _job_.\n\nLes résultats des transformations sont également fournis par le serveur sous forme d\'archive tar _gzippée_.\n\n### Web API\n\n#### GET /transformations/\n\nRetourne une liste JSON des transformations supportées. Exemple:\n\n    ["html2pdf","donothing", "docx2markdown"]\n\n#### POST /job/\n\nAttend dans le corps de la requête une archive de _job_ correctement formée. \n\nRetourne un [UUID version 4](https://fr.wikipedia.org/wiki/Universal_Unique_Identifier#Version_4 "UUID v4") sous \nforme de chaîne de caractères. L\'UUID retourné est l\'identifiant du _job_, à conserver pour les prochaines requêtes.\n\nSi l\'option ```block=1``` est passée dans l\'URL (```/job/?block=1```), alors le comportement est différent: ce n\'est \npas l\'UUID qui sera retourné mais directement le résultat des transformations, de manière identique à ```GET /job/[UUID]```.\n\nEn fonction de la configuration du serveur, la soumission d\'un nouveau _job_ peut nécessiter une authentification. \nDans ce cas, l\'entête HTTP Authorization doit être renseigné sous la forme suivante:\n\n    Authorization: [UUID de l\'application] [signature HMAC de l\'archive]\n\n_Voir le source du client Python pour un exemple de signature HMAC._\n\n#### GET /job/[UUID]\n\nRécupère l\'archive contenant les fichiers transformés par le serveur. Exemple:\n\n    curl http://www.domain.tl/job/55d87fe0-3924-423a-893d-23aa45614ad9 \n\nUn statut HTTP 200 est retourné et l\'archive avec les documents transformés est contenue dans le corps de la réponse.\n\nAu cas où le _job_ ne serait pas terminé, un statut HTTP 202 est retourné.\n\nAu cas où l\'UUID fait référence à un _job_ n\'existant pas sur ce serveur, un statut HTTP 404 est retourné.\n\nEn fonction de la configuration du serveur, la récupération d\'un _job_ terminé peut nécessiter une authentification. \nDans ce cas, l\'entête HTTP Authorization doit être renseigné sous la forme suivante:\n\n    Authorization: [UUID de l\'application] [signature HMAC de l\'UUID du job]\n\n_Voir le source du client Python pour un exemple de signature HMAC._\n\n### Notification\n\nDans le cas où une URL a été fournie dans la clef ```notify_hook``` du fichier job.json, le serveur effectue une \nrequête POST sur cette URL avec l\'UUID du _job_ en corps de requête.\n\n## Serveur de référence\n\nUn serveur de référence est implémenté en Python. Deux composants sont fournis:\n\n- un serveur HTTP exposant l\'interface HTTP du service\n- un pool de workers effectivement chargés des transformations\n\n### Pré-requis\n\n- Python >= 3.6\n\n### Installation et démarrage du service\n\nCréation et activation d\'un venv:\n\n    cd web\n    python3 -m venv myvenv\n    . ./myvenv/bin/activate\n\nInstallation de Circe:\n\n    pip install circe-CERTIC\n\nDémarrage du service HTTP:\n\n    circe serve\n\nDémarrage des workers:\n\n    circe start-workers\n\nDémarrage simultané du service HTTP et des workers:\n\n    circe run\n\n### Variables d\'environnement et configuration par défaut:\n\n- ```CIRCE_HOST``` (```127.0.0.1```)\n- ```CIRCE_PORT``` (```8000```)\n- ```CIRCE_DEBUG``` (```0```)\n- ```CIRCE_WORKERS``` (```number of CPUs```)\n- ```CIRCE_WORKING_DIR``` (```$HOME/.circe/```)\n- ```CIRCE_ENABLE_WEB_UI``` (```0```)\n- ```CIRCE_WEB_UI_CRYPT_KEY``` (```"you should really change this"```)\n- ```CIRCE_WEB_UI_REMOVE_USER_FILES_DELAY``` (```7200```)\n- ```CIRCE_USE_AUTH``` (```1```)\n- ```CIRCE_TRANSFORMATIONS_MODULE``` (```None```)\n\nVous pouvez renseigner ces variables à différents endroits, en fonction de vos besoins:\n\n- dans le fichier lu au démarrage de votre shell (~/.zshrc, ~/.bashrc, etc)\n- dans un fichier .env dans votre répertoire de travail (celui où vous vos trouvez quand vous lancez circe)\n- directement avant la commande circe, sur la même ligne\n\nExemple type d\'un fichier .env:\n\n\tCIRCE_TRANSFORMATIONS_MODULE=mon_module_de_transfos\n\tCIRCE_ENABLE_WEB_UI=1\n\tCIRCE_WEB_UI_CRYPT_KEY="53CreT"\n\tCIRCE_USE_AUTH=0\n\n### Utilisation en ligne de commande\n\nUn certain nombre de commande sont disponibles dans circe. Pour les afficher:\n\n    (venv) ➜  src ✗ circe --help\n    usage: circe [-h]\n                 {serve,start-workers,make-api-access,remove-api-access,list-api-access,list-transformations,run}\n                 ...\n    \n    positional arguments:\n      {serve,start-workers,make-api-access,remove-api-access,list-api-access,list-transformations,run}\n        serve               Start Circe HTTP server\n        start-workers       Start job workers\n        make-api-access     Create new app uuid / secret couple for api access.\n        remove-api-access   Remove access to the API\n        list-api-access     List all access tokens to the API\n        list-transformations\n        run                 Start both HTTP server and job workers\n    \n    optional arguments:\n      -h, --help            show this help message and exit\n\nIl est possible d\'obtenir de l\'aide sur chaque commande:\n\n    (venv) ➜  src ✗ circe serve --help\n    usage: circe serve [-h] [--host HOST] [-p PORT] [-w WORKERS] [-d] [-a]\n\n    Start Circe HTTP server\n\n    optional arguments:\n      -h, --help            show this help message and exit\n      --host HOST           \'127.0.0.1\'\n      -p PORT, --port PORT  8000\n      -w WORKERS, --workers WORKERS\n                            1\n      -d, --debug           False\n      -a, --access-log      False\n\n### Authentification\n\nLorsque la variable d\'environnement CIRCE_USE_AUTH est à "1", le serveur attend\nune authentification sous la forme d\'un hash HMAC du corps de la requête avec\nune clef partagée entre le serveur est le client. Ce hash est ajouté aux entêtes\nde la requête avec un identifiant propre au client de la façon suivante:\n\n\tAuthorization: [identifiant du client] [hash HMAC du corps de la requête]\n\nPour plus de détails sur le hash HMAC, voir les différentes implémentations dans les\nlibrairies clients listées en bas de ce README.\n\nToute la gestion des accès à l\'API Circe se fait en ligne de commande.\n\nPour la création de l\'accès:\n\n\t(venv) ➜  circe-server ✗ circe make-api-access -t MonClientDeTest\n\tAccess granted to MonClientDeTest\n\tuuid    : 33f3f6c5-3bbc-4eeb-b661-e4579b2d9671\n\tsecret: &#H-9csMX|0):\'-eUP6\'u,I6=5X}U|z/\n\nLe client se voit attribué un UUID ainsi qu\'une clef qu\'il utilisera pour le hâchage.\n\nPour la suppression de l\'accès:\n\t\n\t(venv) ➜  circe-server ✗ circe remove-api-access 33f3f6c5-3bbc-4eeb-b661-e4579b2d9671\n\nPour lister tous les accès:\n\n\t(venv) ➜  circe-server ✗ circe list-api-access                                       \n\t7afa5930a3c549b9a7003c0f98b55e73 : rcY<S<"\\oHvjUJdf\'w"J:YKE\\?AKiG"~  [test client]\n\t3a5eb39cadb04c8e9b30ee167c2e4cb5 : ~1!|J;yxGkW)?Z]hQ\\v+Rn*52?`y(_:z  [test client]\n\ta9e836331612498cb1681bc953132d82 : EQbQ&>)BxC|J/5Gz?4$BNr)~&\\|k89`I  [test client]\n\t1f902a30f124450bbe267b56026d347f : L$xc8cwFiczK4z7n%.{eSg^y0xFrzBtX  [test client]\n\n### Ajouter des transformations\n\nLa variable d\'environnement CIRCE_TRANSFORMATIONS_MODULE contient le nom du module Python contenant les transformations\nque vous souhaitez rendre disponible dans le service.\n\nUne transformation est un Python callable (fonction ou classe) prenant en argument le dossier de travail du job, une \ninstance de logging.Logger ainsi qu\'un dictionnaire d\'options (facultatif). Exemple minimal d\'une transformation:\n\n    def ne_fait_rien(working_dir: str, logger: logging.Logger, options: dict = None):\n        pass  # ajouter ici le code transformant les documents\n\nL\'instance de logging.Logger peut prendre ee paramêtre une chaîne ou un dictionnaire:\n\t\n\tlogger.info(\'message de log")\n\tlogger.info({"message": "message de log", "autre info utile": 42}\n\nLes transformations peuvent fournir une description de leur fonctionnement ainsi:\n\n\tne_fait_rien.description = {\n\t\t"label": "Ne fait rien",\n\t\t"help": "Ne fait rien absolument rien. Utile pour tester l\'API.",\n\t\t"options": [],  # aucune option pour cette transformation\n\t}\n\nCes descriptions sont utiles pour les clients.\n\nDes exemples de transformations sont disponibles dans ce dépôt: https://git.unicaen.fr/certic/circe-transformations\n\n## Clients de référence\n\nUne librairie cliente de référence en python est proposée.\n\nInstallation:\n    \n    pip install circe-client-CERTIC\n\nUtilisation:\n\n    from circe_client import Client\n    \n    # Les paramètres peuvent être ignorés si les variables\n    # d\'environnement CIRCE_ENDPOINT, CIRCE_SECRET et CIRCE_APP_UUID\n    # existent.    \n    client = Client(\n        api_endpoint="http://host.tld/,\n        secret_key="notsosecret",\n        application_uuid="786d1b69a6034eb89178fed2a195a1ed",\n    )\n    \n    if "html2pdf" in client.available_transformations():\n        job = client.new_job()\n        job.add_file("index.html")\n        # on peut adjoindre tout fichier utile à la transformation\n        job.add_file("style.css")\n        job.add_transformation("html2pdf")\n        # en option, une URL qui recevra une notification en POST\n        # à la fin du job:\n        # job.set_notify_hook("https://acme.tld/notify-me/")\n    \n        # wait=True pour un appel synchrone à l\'API,\n        # à privilégier pour les jobs courts et/ou les\n        # transformations rapides:\n        client.send(job, wait=True)\n        \n        # pour un appel asynchrone, retournant un UUID de job,\n        # à privilégier pour les jobs longs (transformations lentes\n        # et/ou nombreux fichiers):\n        #\n        # client.send(job)\n        # print(job.uuid)\n        # \n        # On peut ensuite tenter une récupération du job avec\n        # un timeout:\n        #\n        # client.poll(job, timeout=60)\n    \n        # liste les fichiers disponible dans l\'archive de résultat\n        # sous la forme d\'un tuple (nom de fichier, pointeur vers le fichier)\n        for file_name, file_handle in job.result.files:\n            print(file_name)\n\n\nUne [librairie cliente équivalente en Java](https://git.unicaen.fr/certic/circe-java-client) est disponible\nainsi qu\'une [librairie minimale en PHP](https://git.unicaen.fr/certic/circe-php-client) \net un [outil en ligne de commande implémenté en Go](https://git.unicaen.fr/mickael.desfrenes/circe-helper).\n\n## Tests\n\nUn ensemble de tests exécutables par Pytest sont disponibles dans le fichier ```test.py```.\n\n## Mise en production\n\nUne façon simple de faire fonctionner Circe en production sur Linux est de maintenir le service via systemd\net de le mettre en reverse proxy derrière un serveur web.\n\nPour le service sous systemd, ajouter ceci dans un fichier /etc/systemd/system/circe.service, en prenant soin\nde changer les chemins en fonction de votre installation, ainsi que votre utilisateur/groupe:\n\n\t[Unit]\n\tDescription=Circe Service\n\t \n\t[Service]\n\tType=simple\n\t \n\t# ici on a créé un utilisateur spécifique pour le service\n\tUser=circe\n\tGroup=circe\n\tUMask=007\n\t \n\t# On utilise ici le chemin complet vers le script circe installé dans le virtualenv \n\tExecStart=/home/circe/venvs/circe_env/bin/circe run\n\tEnvironment="PATH=/usr/bin:/home/circe/venvs/circe_env/bin:$PATH" \n\t# Vous pouvez changer ici toutes les variables d\'environnement propres à la configuration de Circe\n\tEnvironment="CIRCE_WEB_UI_CRYPT_KEY=qpoiurfIPUBeriIPU"\n\tEnvironment="CIRCE_TRANSFORMATIONS_MODULE=your_transformations"\n\tEnvironment="CIRCE_ENABLE_WEB_UI=0"\n\tRestart=always \n\tRestartSec=0\n\t \n\t# Configures the time to wait before service is stopped forcefully.\n\tTimeoutStopSec=30\n\t\n\t[Install]\n\tWantedBy=multi-user.target\n\nPuis, dans votre shell avec les droits root:\n\t\n\tsystemctl daemon-reload\n\tsystemctl enable circe\n\tsystemctl start circe\n\nVous pouvez vérifier que le service est bien démarré avec la commande suivante:\n\t\n\tsystemctl status circe\n\nLe serveur devrait écouter en locahost (127.0.0.1) sur le port 8000 si vous n\'avez pas modifié sa configuration.\nVous pouvez maintenant placer ce service derrière le serveur web de votre choix.\n\nPour [Caddy](https://caddyserver.com), dans votre /etc/caddy/CaddyFile:\n\n\tcirce.yourhost.com {\n\t\treverse_proxy 127.0.0.1:8000\n\t\theader -Server\n\t}\n\nPour Apache, dans la configuration de votre vhost:\n\n\t<Location />\n\t\tProxyPass "http://127.0.0.1:8000/"\n\t\tProxyPassReverse "http://127.0.0.1:8000/"\n\t</Location>\n\nRéférez-vous à la documentation de votre serveur web pour plus de détail.',
    'author': 'Mickaël Desfrênes',
    'author_email': 'mickael.desfrenes@unicaen.fr',
    'maintainer': None,
    'maintainer_email': None,
    'url': None,
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.9,<4.0',
}


setup(**setup_kwargs)
