From 9c5c71336d3bf4901fa68bca8776b90046c2e779 Mon Sep 17 00:00:00 2001 From: aguyot-ensae Date: Thu, 29 Feb 2024 09:49:04 +0100 Subject: [PATCH] copy files from dsit/datalab --- docker-images-datalab/activetigger/.drone.yml | 48 + docker-images-datalab/activetigger/Dockerfile | 111 + .../activetigger/embed_fasttext.py | 41 + .../activetigger/activetigger/embed_sbert.py | 42 + .../activetigger/activetigger/gobert.py | 174 + .../activetigger/activetigger/gobert_infer.py | 94 + .../activetigger/activetigger/modelnames.csv | 159 + .../activetigger/activetigger/server.R | 4143 +++++++++++++++++ .../activetigger/tokenize_spacy.py | 40 + .../activetigger/activetigger/ui.R | 631 +++ .../activetigger/www/active_tigger.jpeg | Bin 0 -> 106990 bytes .../activetigger/www/active_tigger.png | Bin 0 -> 250946 bytes .../activetigger/activetigger/www/favicon.ico | Bin 0 -> 3758 bytes .../activetigger/requirements.r | 1 + .../activetigger/requirementspython.txt | 6 + docker-images-datalab/index.md | 3 + docker-images-datalab/requirementspython.txt | 15 + .../charts/activetigger/.helmignore | 23 + .../charts/activetigger/Chart.yaml | 24 + .../charts/activetigger/index.md | 63 + .../charts/activetigger/templates/NOTES.txt | 22 + .../activetigger/templates/_helpers.tpl | 62 + .../activetigger/templates/deployment.yaml | 68 + .../charts/activetigger/templates/hpa.yaml | 32 + .../activetigger/templates/ingress.yaml | 61 + .../activetigger/templates/service.yaml | 15 + .../templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../charts/activetigger/values.yaml | 107 + .../charts/overleaf/.helmignore | 23 + .../charts/overleaf/Chart.yaml | 24 + .../helm-charts-test/charts/overleaf/index.md | 20 + .../charts/overleaf/templates/NOTES.txt | 22 + .../charts/overleaf/templates/_helpers.tpl | 62 + .../charts/overleaf/templates/deployment.yaml | 68 + .../charts/overleaf/templates/hpa.yaml | 32 + .../charts/overleaf/templates/ingress.yaml | 61 + .../charts/overleaf/templates/service.yaml | 15 + .../overleaf/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../charts/overleaf/values.yaml | 107 + .../helm-charts-test/charts/test2/.helmignore | 23 + .../helm-charts-test/charts/test2/Chart.yaml | 24 + .../charts/test2/templates/NOTES.txt | 22 + .../charts/test2/templates/_helpers.tpl | 62 + .../charts/test2/templates/deployment.yaml | 68 + .../charts/test2/templates/hpa.yaml | 32 + .../charts/test2/templates/ingress.yaml | 61 + .../charts/test2/templates/service.yaml | 15 + .../test2/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../helm-charts-test/charts/test2/values.yaml | 107 + .../helm-charts-test/charts/test3/.helmignore | 23 + .../helm-charts-test/charts/test3/Chart.yaml | 24 + .../charts/test3/templates/NOTES.txt | 22 + .../charts/test3/templates/_helpers.tpl | 62 + .../charts/test3/templates/deployment.yaml | 68 + .../charts/test3/templates/hpa.yaml | 32 + .../charts/test3/templates/ingress.yaml | 61 + .../charts/test3/templates/service.yaml | 15 + .../test3/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + .../helm-charts-test/charts/test3/values.yaml | 107 + helm-charts-datalab/index.md | 3 + index.md | 5 + kk | 1 - values.yaml | 18 + 67 files changed, 7385 insertions(+), 1 deletion(-) create mode 100644 docker-images-datalab/activetigger/.drone.yml create mode 100644 docker-images-datalab/activetigger/Dockerfile create mode 100644 docker-images-datalab/activetigger/activetigger/embed_fasttext.py create mode 100644 docker-images-datalab/activetigger/activetigger/embed_sbert.py create mode 100644 docker-images-datalab/activetigger/activetigger/gobert.py create mode 100644 docker-images-datalab/activetigger/activetigger/gobert_infer.py create mode 100644 docker-images-datalab/activetigger/activetigger/modelnames.csv create mode 100644 docker-images-datalab/activetigger/activetigger/server.R create mode 100644 docker-images-datalab/activetigger/activetigger/tokenize_spacy.py create mode 100644 docker-images-datalab/activetigger/activetigger/ui.R create mode 100644 docker-images-datalab/activetigger/activetigger/www/active_tigger.jpeg create mode 100644 docker-images-datalab/activetigger/activetigger/www/active_tigger.png create mode 100644 docker-images-datalab/activetigger/activetigger/www/favicon.ico create mode 100644 docker-images-datalab/activetigger/requirements.r create mode 100644 docker-images-datalab/activetigger/requirementspython.txt create mode 100644 docker-images-datalab/index.md create mode 100644 docker-images-datalab/requirementspython.txt create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/.helmignore create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/Chart.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/index.md create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/NOTES.txt create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/_helpers.tpl create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/deployment.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/hpa.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/ingress.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/service.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/serviceaccount.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/templates/tests/test-connection.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/activetigger/values.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/.helmignore create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/Chart.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/index.md create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/NOTES.txt create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/_helpers.tpl create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/deployment.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/hpa.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/ingress.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/service.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/serviceaccount.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/templates/tests/test-connection.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/overleaf/values.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/.helmignore create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/Chart.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/NOTES.txt create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/_helpers.tpl create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/deployment.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/hpa.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/ingress.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/service.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/serviceaccount.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/templates/tests/test-connection.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test2/values.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/.helmignore create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/Chart.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/NOTES.txt create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/_helpers.tpl create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/deployment.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/hpa.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/ingress.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/service.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/serviceaccount.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/templates/tests/test-connection.yaml create mode 100644 helm-charts-datalab/helm-charts-test/charts/test3/values.yaml create mode 100644 helm-charts-datalab/index.md create mode 100644 index.md delete mode 100644 kk create mode 100644 values.yaml diff --git a/docker-images-datalab/activetigger/.drone.yml b/docker-images-datalab/activetigger/.drone.yml new file mode 100644 index 0000000..81a0799 --- /dev/null +++ b/docker-images-datalab/activetigger/.drone.yml @@ -0,0 +1,48 @@ +kind: pipeline +name: Build & publish main + +steps: + - name: publish-image + pull: always + image: plugins/kaniko:1.7.1-kaniko1.9.1 + settings: + auto_tag: true + auto_tag_suffix: latest + registry: code.groupe-genes.fr + repo: code.groupe-genes.fr/datalab/docker-images-datalab/activetigger + username: + from_secret: docker_username + password: + from_secret: docker_password + when: + event: + exclude: + - pull_request + - name: deploy + image: alpine + environment: + kubernetes_server: + from_secret: kubernetes_server + kubernetes_cert: + from_secret: kubernetes_cert + kubernetes_token: + from_secret: kubernetes_token + commands: + - apk add --no-cache curl + - curl -LL -o /usr/bin/kubectl "https://dl.k8s.io/release/v1.28.2/bin/linux/amd64/kubectl" + - curl -LL -o helm.tar.gz "https://get.helm.sh/helm-v3.14.0-linux-amd64.tar.gz" + - tar xf "helm.tar.gz" && mv ./linux-amd64/helm /usr/bin/helm + - chmod +x /usr/bin/kubectl + - chmod +x /usr/bin/helm + - kubectl config set-cluster default --server=$kubernetes_server --insecure-skip-tls-verify=true + - kubectl config set-credentials user --token=$kubernetes_token + - kubectl config set-context default --user=user --cluster=default --namespace=activetigger + - kubectl config use-context default + - kubectl get pods + - helm ls -n activetigger --debug + - helm dependency build ./helm-chart + - helm upgrade activetigger ./helm-chart -f ./helm-chart/values.yaml -n activetigger + when: + event: + exclude: + - pull_request \ No newline at end of file diff --git a/docker-images-datalab/activetigger/Dockerfile b/docker-images-datalab/activetigger/Dockerfile new file mode 100644 index 0000000..651fe7d --- /dev/null +++ b/docker-images-datalab/activetigger/Dockerfile @@ -0,0 +1,111 @@ +FROM ubuntu:22.04 +ARG DEBIAN_FRONTEND=noninteractive + +COPY requirements.r /requirements.r +COPY requirementspython.txt /requirementspython.txt + + +# Installation python +RUN apt-get update && apt-get install -y \ + python3.10 \ + python3.10-distutils \ + python3.10-venv \ + python3-pip \ + r-base \ + wget \ + && apt-get clean + +# Installation R + shiny server +RUN apt-get update && \ + apt-get install -y r-base +RUN R -e "install.packages('shiny', repos='https://cran.rstudio.com/')" + +# Install gdebi-core and shiny-server +RUN apt-get update +RUN apt-get install -y gdebi-core +RUN wget https://download3.rstudio.org/ubuntu-18.04/x86_64/shiny-server-1.5.21.1012-amd64.deb +RUN gdebi --non-interactive shiny-server-1.5.21.1012-amd64.deb + + +## Packages package R (à installer depuis l'exécutable R employé par shiny server) +RUN Rscript /requirements.r + +## Environnement python | a vérifier dans requirementspython.txt l'installation des cu118 se fais de cette manière + + + +# Install Miniconda + RUN wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh && \ + bash Miniconda3-latest-Linux-x86_64.sh -b -p /opt/conda && \ + rm Miniconda3-latest-Linux-x86_64.sh + +# Add Conda binaries to PATH +ENV PATH="/opt/conda/bin:${PATH}" + +# Create a Conda environment and activate it +RUN conda create -n tigger python==3.10 && \ + echo "conda activate tigger" >> ~/.bashrc + +# Mise à jour et installation des dépendances système +RUN rm -rf /var/lib/apt/lists/* + +RUN pip3 install --no-cache-dir \ + torch torchvision torchaudio \ + -f https://download.pytorch.org/whl/cu118/torch_stable.html + +RUN apt-get update && apt-get install -y curl build-essential +RUN pip3 install --no-cache-dir six + +# Install Rust using rustup +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +# Add Cargo's bin directory to the PATH environment variable +ENV PATH="/root/.cargo/bin:${PATH}" + +RUN pip3 install --no-cache-dir --upgrade setuptools + + +# Autres installations de bibliothèques Python +RUN pip3 install argparse +RUN pip3 install datasets +RUN pip3 install fasttext +RUN pip3 install numpy +RUN pip3 install pandas +RUN pip3 install pyarrow +RUN pip3 install scikit-learn +RUN pip3 install sentence-transformers +RUN pip3 install transformers +RUN pip3 install typing-inspect==0.8.0 +RUN pip3 install typing-extensions==4.6.1 +RUN pip3 install spacy + +# Mettre en place des configurations supplémentaires si nécessaire + +# Commande par défaut à exécuter lorsque le conteneur démarre +CMD ["/bin/bash"] + +## Téléchargement des modèles spacy et fasttext + +### Français +WORKDIR ~ +RUN python -m spacy download fr_core_news_sm + +RUN wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fr.300.bin.gz \ + && gunzip cc.fr.300.bin.gz + + +# A chaque création d'instance + +## Clone git pour créer la nouvelle instance (remplacer "tigger-name" par le nom que prendra l'instance, ie https://analytics.huma-num.fr/Prenom.Nom/tigger-name/) +RUN mkdir -p ~/zPublish/shiny/tigger-name +COPY activetigger/ ~/zPublish/shiny/tigger-name + +## Dans l'application + +## Tout en haut à gauche, bouton "+" pour "create project". Puis dans les champs : + +## - data directory: moi j'utilise toujours ~/tagging/domaine (genre ~/tagging/radio ou ~/tagging/journaux), mais c'est à toi de voir où tu veux que les données et tags soient stockées sur ton serveur +## - je conseille de cocher toutes les cases : python, spacy, fasttext, sbert, gpu +## - python : "~/conda/envs/tigger/bin/python" +## - fasttext : "~/cc.fr.300.bin" (càd qu'il faut donner le chemin du modèle sur ton serveur, pas juste le nom) +## - spacy et SBERT : garder les valeurs par défaut pour la langue choisie diff --git a/docker-images-datalab/activetigger/activetigger/embed_fasttext.py b/docker-images-datalab/activetigger/activetigger/embed_fasttext.py new file mode 100644 index 0000000..5c72316 --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/embed_fasttext.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# coding: utf-8 + +## FastText embed sentences +## Requires data file with columns id and text + +import argparse +import fasttext +from os.path import expanduser +import pandas as pd +import pyarrow as pa +import pyarrow.feather as feather +import re + + +def main(args): + print("FastText: Importing data") + datapath = expanduser(args.data) + dat = feather.read_feather(datapath) + outfile = re.sub("[.]feather$", "_ft.feather", datapath) + + print("FastText: Loading model") + ft = fasttext.load_model(expanduser(args.model)) + print("FastText: Embedding sentences") + emb = [ft.get_sentence_vector(re.sub("\n", " ", x)) for x in dat["text"]] + + print("FastText: Exporting") + emb = pd.DataFrame(emb) + emb.columns = ["ft%03d" % (x + 1) for x in range(len(emb.columns))] + emb = pd.concat([dat["id"], emb], axis=1) + feather.write_feather(emb, outfile) + print("FastText: Done") + + +if __name__ == "__main__": + argParser = argparse.ArgumentParser() + argParser.add_argument("-m", "--model", help="Model path", default="/data/user/b/jboelaert/cc.fr.100.bin") + argParser.add_argument("-d", "--data", help="Path to data (feather)") + args = argParser.parse_args() + main(args) + diff --git a/docker-images-datalab/activetigger/activetigger/embed_sbert.py b/docker-images-datalab/activetigger/activetigger/embed_sbert.py new file mode 100644 index 0000000..d944fd9 --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/embed_sbert.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# coding: utf-8 + +## SBERT embed sentences +## Requires data file with columns id and text + +import argparse +from os.path import expanduser +import pandas as pd +import pyarrow as pa +import pyarrow.feather as feather +import re +from sentence_transformers import SentenceTransformer + + +def main(args): + print("SBERT: Importing data") + datapath = expanduser(args.data) + dat = feather.read_feather(datapath) + outfile = re.sub("[.]feather$", "_sb.feather", datapath) + + print("SBERT: Loading model") + sbert = SentenceTransformer(expanduser(args.model)) + sbert.max_seq_length = 512 + print("SBERT: Embedding sentences") + emb = sbert.encode(dat["text"]) + + print("SBERT: Exporting") + emb = pd.DataFrame(emb) + emb.columns = ["sb%03d" % (x + 1) for x in range(len(emb.columns))] + emb = pd.concat([dat["id"], emb], axis=1) + feather.write_feather(emb, outfile) + print("SBERT: Done") + + +if __name__ == "__main__": + argParser = argparse.ArgumentParser() + argParser.add_argument("-m", "--model", help="Model name or path", default="distiluse-base-multilingual-cased-v1") + argParser.add_argument("-d", "--data", help="Path to data (feather)") + args = argParser.parse_args() + main(args) + diff --git a/docker-images-datalab/activetigger/activetigger/gobert.py b/docker-images-datalab/activetigger/activetigger/gobert.py new file mode 100644 index 0000000..32a9f04 --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/gobert.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +# coding: utf-8 + +## BERT trainer to be called by server.R +## Requires two data files with columns id, label and text + +import argparse +import datasets +from datasets import load_metric +import numpy as np +from os.path import expanduser +import os +import pandas as pd +import re +from sklearn import metrics +from transformers import AutoModelForSequenceClassification, AutoTokenizer +from transformers import Trainer, TrainingArguments, TrainerCallback + +os.environ["TOKENIZERS_PARALLELISM"] = "false" + +def main(args): + print("Importing data") + dattrain = pd.read_csv(expanduser(args.traindat)) + datval = pd.read_csv(expanduser(args.valdat)) + datval_id = datval["id"] + classcolname = "label" + + ## Make class_names + class_names = [x for x in dattrain[classcolname].unique()] + + ## Labels to class number + dattrain[classcolname] = [class_names.index(x) for x in dattrain[classcolname].to_list()] + datval[classcolname] = [class_names.index(x) for x in datval[classcolname].to_list()] + + ## Transform to datasets + dattrain = datasets.Dataset.from_pandas(dattrain[['text', 'label']]) + datval = datasets.Dataset.from_pandas(datval[['text', 'label']]) + + # Model choice + modelname = expanduser(args.model) + + ## Tokenizer + print("Tokenizing") + + tokenizer = AutoTokenizer.from_pretrained(modelname) + + # toktrain = dattrain.map(lambda e: tokenizer(e['text'], truncation=True, padding="max_length"), batched=True) + # toktest = datval.map(lambda e: tokenizer(e['text'], truncation=True, padding="max_length"), batched=True) + if args.adapt: + toktrain = dattrain.map(lambda e: tokenizer(e['text'], truncation=True, padding=True, max_length=512), batched=True) + toktest = datval.map(lambda e: tokenizer(e['text'], truncation=True, padding=True, max_length=512), batched=True) + else: + toktrain = dattrain.map(lambda e: tokenizer(e['text'], truncation=True, padding="max_length", max_length=512), batched=True) + toktest = datval.map(lambda e: tokenizer(e['text'], truncation=True, padding="max_length", max_length=512), batched=True) + + del(dattrain) + + ## Model + print("Loading model") + model = AutoModelForSequenceClassification.from_pretrained(modelname, num_labels = len(class_names)) + if (args.gpu): + model.cuda() + + ## Train using Trainer interface + print("Training...") + BATCH_SIZE = args.batchsize + GRAD_ACC = args.gradacc + epochs = args.epochs + + total_steps = (epochs * len(toktrain)) // (BATCH_SIZE * GRAD_ACC) + warmup_steps = (total_steps) // 10 + eval_steps = total_steps // args.eval + + training_args = TrainingArguments( + output_dir=args.session + "_train", + learning_rate=args.lrate, + weight_decay=args.wdecay, + num_train_epochs=epochs, + gradient_accumulation_steps=GRAD_ACC, + per_device_train_batch_size=BATCH_SIZE, + # per_device_eval_batch_size=BATCH_SIZE, + per_device_eval_batch_size=32, + warmup_steps=warmup_steps, + + eval_steps=eval_steps, + evaluation_strategy="steps", + save_strategy="steps", + save_steps=eval_steps, + logging_steps=eval_steps, + do_eval=True, + greater_is_better=False, + load_best_model_at_end=bool(args.best), + metric_for_best_model="eval_loss" + ) + + trainer = Trainer(model=model, args=training_args, + train_dataset=toktrain, eval_dataset=toktest) + + the_session = args.session + class HaltCallback(TrainerCallback): + "A callback that checks for _stop file to interrupt training" + + def on_step_begin(self, args, state, control, **kwargs): + if os.path.exists(the_session + "_stop"): + print("\nHalted by user.\n") + control.should_training_stop = True + return(control) + else: + print("\nNot halted by user.\n") + + trainer.add_callback(HaltCallback) + + trainer.train() + + ## Add class names to model + label_to_id = {v: i for i, v in enumerate(class_names)} + model.config.label2id = label_to_id + model.config.id2label = {id: label for label, id in model.config.label2id.items()} + + ## Save model + model.save_pretrained(args.session) + + + ## Prediction functions + + + def get_predprobs(text): + # inputs = tokenizer(text, padding="max_length", truncation=True, return_tensors="pt") + inputs = tokenizer(text, padding=True, truncation=True, max_length=512, return_tensors="pt") + if (args.gpu): + inputs = inputs.to("cuda") + outputs = model(**inputs) + res = outputs[0] + if (args.gpu): + res = res.cpu() + res = res.softmax(1).detach().numpy() + return res + + + def get_prediction(text): + return class_names[get_predprobs(text).argmax()] + + ## Metrics on validation set + print("Computing predictions") + testpred = [get_prediction(txt) for txt in datval["text"]] + testtruth = [class_names[x] for x in datval["label"]] + + exportpred = pd.DataFrame(datval_id) + exportpred.columns = ["id"] + exportpred["bertpred"] = testpred + exportpred.to_csv(args.session + "_predval.csv", index=False) + + +if __name__ == "__main__": + argParser = argparse.ArgumentParser() + argParser.add_argument("-m", "--model", help="Model name or path", default="microsoft/Multilingual-MiniLM-L12-H384") + argParser.add_argument("-t", "--traindat", help="Path to training data") + argParser.add_argument("-v", "--valdat", help="Path to validation data") + argParser.add_argument("-b", "--batchsize", help="Batch size for training", type=int, default=4) + argParser.add_argument("-g", "--gradacc", help="Gradient accumulation for training", type=int, default=1) + argParser.add_argument("-e", "--epochs", help="Number of training epochs", type=float, default=3) + argParser.add_argument("-l", "--lrate", help="Learning rate", type=float, default=5e-05) + argParser.add_argument("-w", "--wdecay", help="Weight decay", type=float, default=.01) + argParser.add_argument("-B", "--best", help="Load best model instead of last", type=int, choices=[0,1], default=1) + argParser.add_argument("-E", "--eval", help="Number of intermediary evaluations", type=int, default=10) + argParser.add_argument("-s", "--session", help="Session name (used to save results)") + argParser.add_argument("-G", "--gpu", help="Use GPU (CUDA)", type=int, choices=[0,1], default=0) + argParser.add_argument("-A", "--adapt", help="Adapt token length to batch", type=int, choices=[0,1], default=1) + + + args = argParser.parse_args() + + main(args) + diff --git a/docker-images-datalab/activetigger/activetigger/gobert_infer.py b/docker-images-datalab/activetigger/activetigger/gobert_infer.py new file mode 100644 index 0000000..c2c607a --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/gobert_infer.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# coding: utf-8 + +## BERT inference to be called by server.R + +import argparse +import datasets +import json +import numpy as np +from os import path, remove +import pandas as pd +import pyarrow.feather as feather +import re +from torch import no_grad +from transformers import AutoModelForSequenceClassification, AutoTokenizer + + +def chunker(seq, batch_size): + return (seq[pos:pos + batch_size] for pos in range(0, len(seq), batch_size)) + + +def main(args): + print("Importing data") + with open(path.expanduser(args.logfile), "w") as progfile: + progfile.write("Importing data") + + dat = feather.read_feather(path.expanduser(args.dat)) + + with open(path.expanduser(args.logfile), "w") as progfile: + progfile.write("Tokenizing") + + ## Tokenizer + print("Tokenizing") + with open(path.join(path.expanduser(args.model), "config.json"), "r") as jsonfile: + modeltype = json.load(jsonfile)["_name_or_path"] + + tokenizer = AutoTokenizer.from_pretrained(modeltype) + + ## Model + print("Loading model") + model = AutoModelForSequenceClassification.from_pretrained(path.expanduser(args.model)) + if (args.gpu): + model.cuda() + + ## Prediction functions + + + def get_predprobs(text): + inputs = tokenizer(text, padding=True, truncation=True, max_length=512, return_tensors="pt") + if (args.gpu): + inputs = inputs.to("cuda") + with no_grad(): + outputs = model(**inputs) + res = outputs[0] + if (args.gpu): + res = res.cpu() + res = res.softmax(1).detach().numpy() + return res + + print("Computing predictions") + + chunks = chunker([str(x) for x in dat[args.txtname]], args.batch) + pred = [] + for i, x in enumerate(chunks): + if (i % 5 == 0): + percent = round(100 * i * args.batch / len(dat), 1) + logmsg = "Computing: " + str(percent) + "% (" + str(i * args.batch) + "/" + str(len(dat)) + ")" + with open(path.expanduser(args.logfile), "w") as progfile: + progfile.write(logmsg) + pred.append(get_predprobs(x)) + + pred = np.concatenate(pred) + pred = pd.DataFrame(pred) + pred.columns = ["bertpred_" + v for i, v in model.config.id2label.items()] + pred = pd.concat([dat[args.idname], pred], axis=1) + feather.write_feather(pred, path.abspath(args.output)) + remove(path.expanduser(args.logfile)) + + +if __name__ == "__main__": + argParser = argparse.ArgumentParser() + argParser.add_argument("-m", "--model", help="Trained model path") + argParser.add_argument("-d", "--dat", help="Path to data (feather file)") + argParser.add_argument("-o", "--output", help="Output path of predictions", default="tiggerbert.feather") + argParser.add_argument("-i", "--idname", help="Name of id variable", default="id") + argParser.add_argument("-x", "--txtname", help="Name of text variable", default="text") + argParser.add_argument("-l", "--logfile", help="Path to log file", default="tiggerbert-progress.txt") + argParser.add_argument("-G", "--gpu", help="Use GPU (CUDA)", type=int, choices=[0,1], default=1) + argParser.add_argument("-b", "--batch", help="Batch size", type=int, default=128) + + args = argParser.parse_args() + + main(args) + diff --git a/docker-images-datalab/activetigger/activetigger/modelnames.csv b/docker-images-datalab/activetigger/activetigger/modelnames.csv new file mode 100644 index 0000000..a49e67e --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/modelnames.csv @@ -0,0 +1,159 @@ +"short","spacy_name","fasttext_name","fasttext_url","language","short_lang" +"af","xx_ent_wiki_sm","cc.af.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.af.300.bin.gz","Afrikaans","(af) Afrikaans" +"als","xx_ent_wiki_sm","cc.als.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.als.300.bin.gz","Alemannic","(als) Alemannic" +"am","xx_ent_wiki_sm","cc.am.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.am.300.bin.gz","Amharic","(am) Amharic" +"an","xx_ent_wiki_sm","cc.an.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.an.300.bin.gz","Aragonese","(an) Aragonese" +"ar","xx_ent_wiki_sm","cc.ar.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ar.300.bin.gz","Arabic","(ar) Arabic" +"arz","xx_ent_wiki_sm","cc.arz.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.arz.300.bin.gz","Egyptian Arabic","(arz) Egyptian Arabic" +"as","xx_ent_wiki_sm","cc.as.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.as.300.bin.gz","Assamese","(as) Assamese" +"ast","xx_ent_wiki_sm","cc.ast.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ast.300.bin.gz","Asturian","(ast) Asturian" +"az","xx_ent_wiki_sm","cc.az.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.az.300.bin.gz","Azerbaijani","(az) Azerbaijani" +"azb","xx_ent_wiki_sm","cc.azb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.azb.300.bin.gz","Southern Azerbaijani","(azb) Southern Azerbaijani" +"ba","xx_ent_wiki_sm","cc.ba.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ba.300.bin.gz","Bashkir","(ba) Bashkir" +"bar","xx_ent_wiki_sm","cc.bar.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bar.300.bin.gz","Bavarian","(bar) Bavarian" +"bcl","xx_ent_wiki_sm","cc.bcl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bcl.300.bin.gz","Central Bicolano","(bcl) Central Bicolano" +"be","xx_ent_wiki_sm","cc.be.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.be.300.bin.gz","Belarusian","(be) Belarusian" +"bg","xx_ent_wiki_sm","cc.bg.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bg.300.bin.gz","Bulgarian","(bg) Bulgarian" +"bh","xx_ent_wiki_sm","cc.bh.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bh.300.bin.gz","Bihari","(bh) Bihari" +"bn","xx_ent_wiki_sm","cc.bn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bn.300.bin.gz","Bengali","(bn) Bengali" +"bo","xx_ent_wiki_sm","cc.bo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bo.300.bin.gz","Tibetan","(bo) Tibetan" +"bpy","xx_ent_wiki_sm","cc.bpy.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bpy.300.bin.gz","Bishnupriya Manipuri","(bpy) Bishnupriya Manipuri" +"br","xx_ent_wiki_sm","cc.br.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.br.300.bin.gz","Breton","(br) Breton" +"bs","xx_ent_wiki_sm","cc.bs.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.bs.300.bin.gz","Bosnian","(bs) Bosnian" +"ca","ca_core_news_sm","cc.ca.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ca.300.bin.gz","Catalan","(ca) Catalan" +"ce","xx_ent_wiki_sm","cc.ce.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ce.300.bin.gz","Chechen","(ce) Chechen" +"ceb","xx_ent_wiki_sm","cc.ceb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ceb.300.bin.gz","Cebuano","(ceb) Cebuano" +"ckb","xx_ent_wiki_sm","cc.ckb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ckb.300.bin.gz","Kurdish (Sorani)","(ckb) Kurdish (Sorani)" +"co","xx_ent_wiki_sm","cc.co.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.co.300.bin.gz","Corsican","(co) Corsican" +"cs","xx_ent_wiki_sm","cc.cs.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.cs.300.bin.gz","Czech","(cs) Czech" +"cv","xx_ent_wiki_sm","cc.cv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.cv.300.bin.gz","Chuvash","(cv) Chuvash" +"cy","xx_ent_wiki_sm","cc.cy.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.cy.300.bin.gz","Welsh","(cy) Welsh" +"da","da_core_news_sm","cc.da.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.da.300.bin.gz","Danish","(da) Danish" +"de","de_core_news_sm","cc.de.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.de.300.bin.gz","German","(de) German" +"diq","xx_ent_wiki_sm","cc.diq.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.diq.300.bin.gz","Zazaki","(diq) Zazaki" +"dv","xx_ent_wiki_sm","cc.dv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.dv.300.bin.gz","Divehi","(dv) Divehi" +"el","el_core_news_sm","cc.el.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.el.300.bin.gz","Greek","(el) Greek" +"eml","xx_ent_wiki_sm","cc.eml.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.eml.300.bin.gz","Emilian-Romagnol","(eml) Emilian-Romagnol" +"en","en_core_web_sm","cc.en.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.en.300.bin.gz","English","(en) English" +"eo","xx_ent_wiki_sm","cc.eo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.eo.300.bin.gz","Esperanto","(eo) Esperanto" +"es","es_core_news_sm","cc.es.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.es.300.bin.gz","Spanish","(es) Spanish" +"et","xx_ent_wiki_sm","cc.et.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.et.300.bin.gz","Estonian","(et) Estonian" +"eu","xx_ent_wiki_sm","cc.eu.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.eu.300.bin.gz","Basque","(eu) Basque" +"fa","xx_ent_wiki_sm","cc.fa.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fa.300.bin.gz","Persian","(fa) Persian" +"fi","fi_core_news_sm","cc.fi.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fi.300.bin.gz","Finnish","(fi) Finnish" +"fr","fr_core_news_sm","cc.fr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fr.300.bin.gz","French","(fr) French" +"frr","xx_ent_wiki_sm","cc.frr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.frr.300.bin.gz","North Frisian","(frr) North Frisian" +"fy","xx_ent_wiki_sm","cc.fy.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fy.300.bin.gz","West Frisian","(fy) West Frisian" +"ga","xx_ent_wiki_sm","cc.ga.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ga.300.bin.gz","Irish","(ga) Irish" +"gd","xx_ent_wiki_sm","cc.gd.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.gd.300.bin.gz","Scottish Gaelic","(gd) Scottish Gaelic" +"gl","xx_ent_wiki_sm","cc.gl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.gl.300.bin.gz","Galician","(gl) Galician" +"gom","xx_ent_wiki_sm","cc.gom.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.gom.300.bin.gz","Goan Konkani","(gom) Goan Konkani" +"gu","xx_ent_wiki_sm","cc.gu.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.gu.300.bin.gz","Gujarati","(gu) Gujarati" +"gv","xx_ent_wiki_sm","cc.gv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.gv.300.bin.gz","Manx","(gv) Manx" +"he","xx_ent_wiki_sm","cc.he.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.he.300.bin.gz","Hebrew","(he) Hebrew" +"hi","xx_ent_wiki_sm","cc.hi.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hi.300.bin.gz","Hindi","(hi) Hindi" +"hif","xx_ent_wiki_sm","cc.hif.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hif.300.bin.gz","Fiji Hindi","(hif) Fiji Hindi" +"hr","hr_core_news_sm","cc.hr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hr.300.bin.gz","Croatian","(hr) Croatian" +"hsb","xx_ent_wiki_sm","cc.hsb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hsb.300.bin.gz","Upper Sorbian","(hsb) Upper Sorbian" +"ht","xx_ent_wiki_sm","cc.ht.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ht.300.bin.gz","Haitian","(ht) Haitian" +"hu","xx_ent_wiki_sm","cc.hu.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hu.300.bin.gz","Hungarian","(hu) Hungarian" +"hy","xx_ent_wiki_sm","cc.hy.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.hy.300.bin.gz","Armenian","(hy) Armenian" +"ia","xx_ent_wiki_sm","cc.ia.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ia.300.bin.gz","Interlingua","(ia) Interlingua" +"id","xx_ent_wiki_sm","cc.id.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.id.300.bin.gz","Indonesian","(id) Indonesian" +"ilo","xx_ent_wiki_sm","cc.ilo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ilo.300.bin.gz","Ilokano","(ilo) Ilokano" +"io","xx_ent_wiki_sm","cc.io.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.io.300.bin.gz","Ido","(io) Ido" +"is","xx_ent_wiki_sm","cc.is.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.is.300.bin.gz","Icelandic","(is) Icelandic" +"it","it_core_news_sm","cc.it.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.it.300.bin.gz","Italian","(it) Italian" +"ja","ja_core_news_sm","cc.ja.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ja.300.bin.gz","Japanese","(ja) Japanese" +"jv","xx_ent_wiki_sm","cc.jv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.jv.300.bin.gz","Javanese","(jv) Javanese" +"ka","xx_ent_wiki_sm","cc.ka.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ka.300.bin.gz","Georgian","(ka) Georgian" +"kk","xx_ent_wiki_sm","cc.kk.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.kk.300.bin.gz","Kazakh","(kk) Kazakh" +"km","xx_ent_wiki_sm","cc.km.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.km.300.bin.gz","Khmer","(km) Khmer" +"kn","xx_ent_wiki_sm","cc.kn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.kn.300.bin.gz","Kannada","(kn) Kannada" +"ko","ko_core_news_sm","cc.ko.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ko.300.bin.gz","Korean","(ko) Korean" +"ku","xx_ent_wiki_sm","cc.ku.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ku.300.bin.gz","Kurdish (Kurmanji)","(ku) Kurdish (Kurmanji)" +"ky","xx_ent_wiki_sm","cc.ky.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ky.300.bin.gz","Kirghiz","(ky) Kirghiz" +"la","xx_ent_wiki_sm","cc.la.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.la.300.bin.gz","Latin","(la) Latin" +"lb","xx_ent_wiki_sm","cc.lb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.lb.300.bin.gz","Luxembourgish","(lb) Luxembourgish" +"li","xx_ent_wiki_sm","cc.li.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.li.300.bin.gz","Limburgish","(li) Limburgish" +"lmo","xx_ent_wiki_sm","cc.lmo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.lmo.300.bin.gz","Lombard","(lmo) Lombard" +"lt","lt_core_news_sm","cc.lt.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.lt.300.bin.gz","Lithuanian","(lt) Lithuanian" +"lv","xx_ent_wiki_sm","cc.lv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.lv.300.bin.gz","Latvian","(lv) Latvian" +"mai","xx_ent_wiki_sm","cc.mai.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mai.300.bin.gz","Maithili","(mai) Maithili" +"mg","xx_ent_wiki_sm","cc.mg.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mg.300.bin.gz","Malagasy","(mg) Malagasy" +"mhr","xx_ent_wiki_sm","cc.mhr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mhr.300.bin.gz","Meadow Mari","(mhr) Meadow Mari" +"min","xx_ent_wiki_sm","cc.min.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.min.300.bin.gz","Minangkabau","(min) Minangkabau" +"mk","mk_core_news_sm","cc.mk.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mk.300.bin.gz","Macedonian","(mk) Macedonian" +"ml","xx_ent_wiki_sm","cc.ml.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ml.300.bin.gz","Malayalam","(ml) Malayalam" +"mn","xx_ent_wiki_sm","cc.mn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mn.300.bin.gz","Mongolian","(mn) Mongolian" +"mr","xx_ent_wiki_sm","cc.mr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mr.300.bin.gz","Marathi","(mr) Marathi" +"mrj","xx_ent_wiki_sm","cc.mrj.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mrj.300.bin.gz","Hill Mari","(mrj) Hill Mari" +"ms","xx_ent_wiki_sm","cc.ms.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ms.300.bin.gz","Malay","(ms) Malay" +"mt","xx_ent_wiki_sm","cc.mt.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mt.300.bin.gz","Maltese","(mt) Maltese" +"mwl","xx_ent_wiki_sm","cc.mwl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mwl.300.bin.gz","Mirandese","(mwl) Mirandese" +"my","xx_ent_wiki_sm","cc.my.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.my.300.bin.gz","Burmese","(my) Burmese" +"myv","xx_ent_wiki_sm","cc.myv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.myv.300.bin.gz","Erzya","(myv) Erzya" +"mzn","xx_ent_wiki_sm","cc.mzn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.mzn.300.bin.gz","Mazandarani","(mzn) Mazandarani" +"nah","xx_ent_wiki_sm","cc.nah.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nah.300.bin.gz","Nahuatl","(nah) Nahuatl" +"nap","xx_ent_wiki_sm","cc.nap.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nap.300.bin.gz","Neapolitan","(nap) Neapolitan" +"nds","xx_ent_wiki_sm","cc.nds.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nds.300.bin.gz","Low Saxon","(nds) Low Saxon" +"ne","xx_ent_wiki_sm","cc.ne.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ne.300.bin.gz","Nepali","(ne) Nepali" +"new","xx_ent_wiki_sm","cc.new.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.new.300.bin.gz","Newar","(new) Newar" +"nl","nl_core_news_sm","cc.nl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nl.300.bin.gz","Dutch","(nl) Dutch" +"nn","xx_ent_wiki_sm","cc.nn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nn.300.bin.gz","Norwegian (Nynorsk)","(nn) Norwegian (Nynorsk)" +"no","xx_ent_wiki_sm","cc.no.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.no.300.bin.gz","Norwegian (Bokmål)","(no) Norwegian (Bokmål)" +"nso","xx_ent_wiki_sm","cc.nso.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.nso.300.bin.gz","Northern Sotho","(nso) Northern Sotho" +"oc","xx_ent_wiki_sm","cc.oc.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.oc.300.bin.gz","Occitan","(oc) Occitan" +"or","xx_ent_wiki_sm","cc.or.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.or.300.bin.gz","Oriya","(or) Oriya" +"os","xx_ent_wiki_sm","cc.os.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.os.300.bin.gz","Ossetian","(os) Ossetian" +"pa","xx_ent_wiki_sm","cc.pa.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pa.300.bin.gz","Eastern Punjabi","(pa) Eastern Punjabi" +"pam","xx_ent_wiki_sm","cc.pam.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pam.300.bin.gz","Kapampangan","(pam) Kapampangan" +"pfl","xx_ent_wiki_sm","cc.pfl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pfl.300.bin.gz","Palatinate German","(pfl) Palatinate German" +"pl","pl_core_news_sm","cc.pl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pl.300.bin.gz","Polish","(pl) Polish" +"pms","xx_ent_wiki_sm","cc.pms.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pms.300.bin.gz","Piedmontese","(pms) Piedmontese" +"pnb","xx_ent_wiki_sm","cc.pnb.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pnb.300.bin.gz","Western Punjabi","(pnb) Western Punjabi" +"ps","xx_ent_wiki_sm","cc.ps.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ps.300.bin.gz","Pashto","(ps) Pashto" +"pt","pt_core_news_sm","cc.pt.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.pt.300.bin.gz","Portuguese","(pt) Portuguese" +"qu","xx_ent_wiki_sm","cc.qu.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.qu.300.bin.gz","Quechua","(qu) Quechua" +"rm","xx_ent_wiki_sm","cc.rm.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.rm.300.bin.gz","Romansh","(rm) Romansh" +"ro","ro_core_news_sm","cc.ro.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ro.300.bin.gz","Romanian","(ro) Romanian" +"ru","ru_core_news_sm","cc.ru.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ru.300.bin.gz","Russian","(ru) Russian" +"sa","xx_ent_wiki_sm","cc.sa.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sa.300.bin.gz","Sanskrit","(sa) Sanskrit" +"sah","xx_ent_wiki_sm","cc.sah.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sah.300.bin.gz","Sakha","(sah) Sakha" +"sc","xx_ent_wiki_sm","cc.sc.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sc.300.bin.gz","Sardinian","(sc) Sardinian" +"scn","xx_ent_wiki_sm","cc.scn.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.scn.300.bin.gz","Sicilian","(scn) Sicilian" +"sco","xx_ent_wiki_sm","cc.sco.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sco.300.bin.gz","Scots","(sco) Scots" +"sd","xx_ent_wiki_sm","cc.sd.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sd.300.bin.gz","Sindhi","(sd) Sindhi" +"sh","xx_ent_wiki_sm","cc.sh.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sh.300.bin.gz","Serbo-Croatian","(sh) Serbo-Croatian" +"si","xx_ent_wiki_sm","cc.si.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.si.300.bin.gz","Sinhalese","(si) Sinhalese" +"sk","xx_ent_wiki_sm","cc.sk.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sk.300.bin.gz","Slovak","(sk) Slovak" +"sl","sl_core_news_sm","cc.sl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sl.300.bin.gz","Slovenian","(sl) Slovenian" +"so","xx_ent_wiki_sm","cc.so.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.so.300.bin.gz","Somali","(so) Somali" +"sq","xx_ent_wiki_sm","cc.sq.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sq.300.bin.gz","Albanian","(sq) Albanian" +"sr","xx_ent_wiki_sm","cc.sr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sr.300.bin.gz","Serbian","(sr) Serbian" +"su","xx_ent_wiki_sm","cc.su.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.su.300.bin.gz","Sundanese","(su) Sundanese" +"sv","sv_core_news_sm","cc.sv.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sv.300.bin.gz","Swedish","(sv) Swedish" +"sw","xx_ent_wiki_sm","cc.sw.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.sw.300.bin.gz","Swahili","(sw) Swahili" +"ta","xx_ent_wiki_sm","cc.ta.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ta.300.bin.gz","Tamil","(ta) Tamil" +"te","xx_ent_wiki_sm","cc.te.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.te.300.bin.gz","Telugu","(te) Telugu" +"tg","xx_ent_wiki_sm","cc.tg.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.tg.300.bin.gz","Tajik","(tg) Tajik" +"th","xx_ent_wiki_sm","cc.th.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.th.300.bin.gz","Thai","(th) Thai" +"tk","xx_ent_wiki_sm","cc.tk.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.tk.300.bin.gz","Turkmen","(tk) Turkmen" +"tl","xx_ent_wiki_sm","cc.tl.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.tl.300.bin.gz","Tagalog","(tl) Tagalog" +"tr","xx_ent_wiki_sm","cc.tr.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.tr.300.bin.gz","Turkish","(tr) Turkish" +"tt","xx_ent_wiki_sm","cc.tt.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.tt.300.bin.gz","Tatar","(tt) Tatar" +"ug","xx_ent_wiki_sm","cc.ug.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ug.300.bin.gz","Uyghur","(ug) Uyghur" +"uk","uk_core_news_sm","cc.uk.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.uk.300.bin.gz","Ukrainian","(uk) Ukrainian" +"ur","xx_ent_wiki_sm","cc.ur.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ur.300.bin.gz","Urdu","(ur) Urdu" +"uz","xx_ent_wiki_sm","cc.uz.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.uz.300.bin.gz","Uzbek","(uz) Uzbek" +"vec","xx_ent_wiki_sm","cc.vec.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.vec.300.bin.gz","Venetian","(vec) Venetian" +"vi","xx_ent_wiki_sm","cc.vi.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.vi.300.bin.gz","Vietnamese","(vi) Vietnamese" +"vls","xx_ent_wiki_sm","cc.vls.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.vls.300.bin.gz","West Flemish","(vls) West Flemish" +"vo","xx_ent_wiki_sm","cc.vo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.vo.300.bin.gz","Volapük","(vo) Volapük" +"wa","xx_ent_wiki_sm","cc.wa.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.wa.300.bin.gz","Walloon","(wa) Walloon" +"war","xx_ent_wiki_sm","cc.war.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.war.300.bin.gz","Waray","(war) Waray" +"xmf","xx_ent_wiki_sm","cc.xmf.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.xmf.300.bin.gz","Mingrelian","(xmf) Mingrelian" +"yi","xx_ent_wiki_sm","cc.yi.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.yi.300.bin.gz","Yiddish","(yi) Yiddish" +"yo","xx_ent_wiki_sm","cc.yo.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.yo.300.bin.gz","Yoruba","(yo) Yoruba" +"zea","xx_ent_wiki_sm","cc.zea.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.zea.300.bin.gz","Zeelandic","(zea) Zeelandic" +"zh","zh_core_web_sm","cc.zh.300.bin","https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.zh.300.bin.gz","Chinese","(zh) Chinese" diff --git a/docker-images-datalab/activetigger/activetigger/server.R b/docker-images-datalab/activetigger/activetigger/server.R new file mode 100644 index 0000000..89a7ae6 --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/server.R @@ -0,0 +1,4143 @@ + +pkgs <- c("arrow", "class", "DT", "future", "ggplot2", "glmnet", "htmlTable", + "LiblineaR", "Matrix","Metrics", "promises", "quanteda", + "quanteda.textmodels", "ranger", "rlang", "Rtsne", + "RJSONIO", "shiny", "SparseM", "stringi", "uwot") +pkgs <- pkgs[! pkgs %in% rownames(installed.packages())] +if (length(pkgs)) { + warning(paste0("Packages ", paste0(pkgs, collapse = " , "), " not available. ", + "App might crash.")) +} +# for (i in seq_along(pkgs)) install.packages(pkgs[i]) + +verbose <- TRUE +# sink("ActiveTigger.log") + +options(shiny.maxRequestSize = 30*1024^3) # File upload size + +modelnames <- read.csv("modelnames.csv") +modelnames_labels <- modelnames$short +names(modelnames_labels) <- modelnames$short_lang + +library(quanteda) +library(ggplot2) +library(promises) +library(future) +plan(multisession) + + +################################################################################ +## Global functions +################################################################################ + +label2hash <- function(label) rlang::hash(label) + +cleanFileName <- function(name) + gsub('\\s|[/]|[\\]|[:]|[*]|[?]|["]|[<]|[>]|[+]|[$]|[=]', "_", + gsub("^\\s+|\\s+$", "", name)) + +wtdF1 <- function(truth, pred) { + pred <- pred[!is.na(truth)] + truth <- truth[!is.na(truth)] + sum(sapply(unique(truth), function(ilab) + Metrics::fbeta_score(truth == ilab, pred == ilab) * + sum(truth == ilab) / length(truth)), na.rm = TRUE) +} + +metricsTable <- function(truth, pred) { + do.call(rbind, lapply(sort(unique(truth)), function(ilab) { + tmptrue <- truth == ilab + tmppred <- pred == ilab + tmp <- data.frame( + "Tag" = ilab, + "N.cases" = sum(tmptrue), + "N.predict" = sum(tmppred), + "Accuracy" = sprintf("%f", round(Metrics::accuracy(tmptrue, tmppred), 6)), + "Precision" = sprintf("%f", round(Metrics::precision(tmptrue, tmppred), 6)), + "Recall" = sprintf("%f", round(Metrics::recall(tmptrue, tmppred), 6)), + "F1" = sprintf("%f", round(Metrics::fbeta_score(tmptrue, tmppred), 6))) + tmp$F1[tmp$F1 == "NaN"] <- sprintf("%f", 0) + tmp + })) +} + +bertPlotFromPath <- function(path) { + if (!file.exists(paste0(path, "/trainer_state.json"))) return(NULL) + state <- RJSONIO::fromJSON(paste0(path, "/trainer_state.json")) + logs <- data.table::rbindlist(lapply(state$log_history, as.list), fill = TRUE) + epochmin <- logs$epoch[which.min(logs$eval_loss)] + + gdat <- data.table::rbindlist( + use.names = FALSE, + list( + cbind("train", logs[!is.na(logs$loss), c("epoch", "loss")]), + cbind("valid", logs[!is.na(logs$eval_loss), c("epoch", "eval_loss")]) + )) + colnames(gdat) <- c("data", "epoch", "loss") + + ggplot(gdat, aes_string("epoch", "loss", color = "data", shape = "data")) + + geom_point(size = 2) + geom_line() + + geom_vline(xintercept = epochmin, linetype = 2) + + theme_bw() + expand_limits(y=0) +} + +bertHyperToMsg <- function(pars) { + if (is.null(pars)) return(NULL) + paste0("Scheme : ", pars$bertScheme, "; ", + "Model : ", pars$bertModel, "\n* ", + "Epochs : ", pars$bertEpochs, " ; ", + "Lrate : ", pars$bertLrate, " ; ", + "Wdecay : ", pars$bertWdecay, " ; ", + "Batch size : ", pars$bertBatchsize, " ; ", + "Grad. accum. : ", pars$bertGradacc, "\n* ", + "Keep best : ", pars$bertBest, " ; ", + "ValidFrac : ", pars$bertValidFrac, " ; ", + "ValidSeed : ", pars$bertValidSeed, " \n* ", + pars$bertNtrain, " training samples : ", + paste0(names(pars$bertTrainTable), " (", + as.numeric(pars$bertTrainTable), ")", + collapse = " ; ")) +} + +get_status <- function(dafile) + scan(dafile, what = "character", sep="\n", quiet = TRUE) +set_status <- function(dafile, msg) write(msg, dafile) + + + +################################################################################ +## Main server function +################################################################################ + +# options(shiny.maxRequestSize=2^34) # Max filesize + +shinyServer(function(input, output, session) { + + values <- reactiveValues() + values$visuGo <- FALSE + values$dfmGo <- FALSE + # queryTrigger <- reactiveVal(0) + # predlabTrigger <- reactiveVal(0) + initDataTrigger <- reactiveVal(0) + queryNext <- reactiveVal(0) + trainTrigger <- reactiveVal(0) + predTrigger <- reactiveVal(0) + diagTrigger <- reactiveVal(0) + ready2tag <- reactiveVal(0) + trainCountdown <- reactiveVal(0) + + ok.data <- reactiveVal() + ok.data.async <- reactiveVal() + ok.data.running <- reactiveVal(FALSE) + + bpall_nclicks <- reactiveVal(0) + bertpred_nclicks <- reactiveVal(0) + + ## NEW use_regressors list as values$new_use_regressors + observeEvent(input$use_regressors, { + values$new_use_regressors <- input$use_regressors + cat("DEBUG new_use_regressors: ", paste(input$use_regressors, collapse = " "), "\n") + }) + + ## NEW trainCountdown with input$trainCountdown + ## Policy: 0, don't train ; 1, trigger train on tag ; >1, decrement on tag + observeEvent(input$trainCountdown, trainCountdown(input$trainCountdown)) + + #################### + ## Projects and data + + ## Read config files, update UI forms accordingly + ## This should happen only once + observe({ + if (verbose) + cat("DEBUG read project configs\n") + # values$conf <- RJSONIO::fromJSON("conf.json") + projfiles <- dir(pattern = "^tigger_.*[.]json$") + isolate({ + if (!length(projfiles)) { + values$conf <- NULL + return(NULL) + } + values$confnames <- gsub("^tigger_(.*)[.]json$", "\\1", projfiles) + + values$conf <- RJSONIO::fromJSON(projfiles[1]) + }) + }) + + ## When projects added, update selector + observeEvent(values$confnames, { + if (verbose) + cat("DEBUG update selectProject from confnames\n") + updateSelectInput(session, "selectProject", choices = values$confnames) + }) + + ## Event from project change + observeEvent(input$selectProject, { + if (is.null(input$selectProject)) return(NULL) + if (!nchar(input$selectProject)) return(NULL) + if (verbose) + cat("DEBUG project change step 1\n") + # values$project <- input$selectProject + # values$conf <- values$confs[[values$project]] + values$conf <- RJSONIO::fromJSON( + paste0("tigger_", input$selectProject, ".json")) + ok.data(NULL) + ok.visu(NULL) + initDataTrigger(initDataTrigger() + 1) + + if (verbose) + cat("DEBUG project change step 2\n") + if (!is.null(values$conf$dataNrows)) + updateNumericInput(session, "dataNrows", value = as.numeric(values$conf$dataNrows)) + if (!is.null(values$conf$dataSkipRows)) + updateNumericInput(session, "dataSkipRows", value = as.numeric(values$conf$dataSkipRows)) + + #### Python #### + ## Default use_python if not found in conf file: FALSE + if(is.null(values$conf$use_python)) + values$conf$use_python <- FALSE + updateCheckboxInput(session, "sys_use_python", value = values$conf$use_python) + + ## Set default python if not found in conf file + if(is.null(values$conf$python)) + values$conf$python <- "python3" + updateTextInput(session, "sys_which_python", value = values$conf$python) + + values$python_ok <- FALSE + if (values$conf$use_python) { + ## Check whether python is working + pytest <- try(system(paste(values$conf$python, "--version"), + intern = TRUE)) + if (inherits(pytest, "try-error")) { + showNotification(paste( + "Python path `", values$conf$python, + "` not valid, try changing it in Project/System tab"), + duration = 10, type = "error") + } else + values$python_ok <- TRUE + } + if (verbose) + cat("Python ok:", values$python_ok, "\n") + + + #### GPU #### + if (!is.null(values$conf$use_gpu)) + updateCheckboxInput(session, "sys_use_gpu", value = values$conf$use_gpu) + + #### Spacy #### + if (!is.null(values$conf$use_spacy)) + updateCheckboxInput(session, "sys_use_spacy", value = values$conf$use_spacy) + updateTextInput(session, "sys_use_spacy_model", value = values$conf$use_spacy_model) + + #### fastText #### + if (!is.null(values$conf$use_ft)) + updateCheckboxInput(session, "sys_use_ft", value = values$conf$use_ft) + updateTextInput(session, "sys_use_ft_model", value = values$conf$use_ft_model) + + #### SBERT #### + if (!is.null(values$conf$use_sb)) + updateCheckboxInput(session, "sys_use_sb", value = values$conf$use_sb) + updateTextInput(session, "sys_use_sb_model", value = values$conf$use_sb_model) + + }) + + + ############################################################################## + ## Reactives : project settings UI + ############################################################################## + + output$sys_datadir <- renderText(values$conf$datadir) + output$sys_datafile <- renderText(paste0(values$conf$dataname, ".feather")) + output$sys_var_id <- renderUI(renderText(values$conf$idcol)) + output$sys_var_text <- renderUI(renderText(values$conf$textcols)) + output$sys_var_tag <- renderUI(renderText(values$conf$tagcols)) + output$sys_var_context_ui <- renderUI(selectInput( + "sys_var_context", NULL, values$data_orig_colnames, values$conf$contextcols, + multiple = TRUE)) + output$sys_var_comm_ui <- renderUI(selectInput( + "sys_var_comm", NULL, c("(none)", values$data_orig_colnames), + values$conf$commcol)) + + output$sys_ex_lang_ui <- renderUI(selectInput( + "sys_ex_lang", NULL, modelnames_labels, "en" + )) + output$sys_ex_spacy <- renderText( + modelnames$spacy_name[modelnames$short == input$sys_ex_lang]) + output$sys_ex_spacy_dl <- renderText(paste0( + "Download with python module: ", values$conf$python, " -m spacy download ", + modelnames$spacy_name[modelnames$short == input$sys_ex_lang])) + output$sys_ex_ft <- renderText( + modelnames$fasttext_name[modelnames$short == input$sys_ex_lang]) + output$sys_ex_ft_dl <- renderText(paste0( + "Manual download link : ", + modelnames$fasttext_url[modelnames$short == input$sys_ex_lang])) + output$sys_ex_sb <- renderText( + ifelse(input$sys_ex_lang %in% c("ar", "zh", "nl", "en", "fr", "de", + "it", "ko", "pl", "pt", "ru", "es", "tr"), + "distiluse-base-multilingual-cased-v1", + "distiluse-base-multilingual-cased-v2")) + + + ## Save system changes on button click, with modal + output$save_config_msg <- renderUI({ + req(values$save_config_msg) + p(strong(values$save_config_msg)) + }) + output$save_embed_msg <- renderUI({ + tmp <- "" + if (input$sys_use_spacy) if (!identical(input$sys_use_spacy_model, values$conf$use_spacy_model)) + tmp <- paste0(tmp, "
  • Changing the spacy model will delete the current tokenized text and fasttext word embeddings, to prevent conflicts
  • ") + if (input$sys_use_ft) if (!identical(input$sys_use_ft_model, values$conf$use_ft_model)) + tmp <- paste0(tmp, "
  • Changing the fasttext model will delete the current fasttext word embeddings, to prevent conflicts.
  • ") + if (input$sys_use_sb) if (!identical(input$sys_use_sb_model, values$conf$use_sb_model)) + tmp <- paste0(tmp, "
  • Changing the SBERT model will delete the current SBERT sentence embeddings, to prevent conflicts.
  • ") + + if (nchar(tmp)) { + return(HTML(paste("

    Warning:

    "))) + } else + return(NULL) + }) + observeEvent(input$saveSystem, { + values$save_config_msg <- NULL + showModal(modalDialog( + title = "Save project configuration", + paste0("Click 'Save' to check and confirm the new configuration"), + uiOutput("save_embed_msg"), + uiOutput("save_config_msg"), + footer = tagList(actionButton(paste0("save_conf_confirm"), "Save"), + modalButton("Cancel")))) + }) + observeEvent(input$save_conf_confirm, { + newconf <- values$conf + newconf$contextcols <- input$sys_var_context + newconf$commcol <- input$sys_var_comm + + if (input$sys_use_python) { + pytest <- try(system(paste(input$sys_which_python, "--version"), + intern = TRUE)) + if (inherits(pytest, "try-error")) { + values$save_config_msg <- paste0( + "Error: python path `", input$sys_which_python, "` not valid") + return(NULL) + } + } + newconf$use_python <- input$sys_use_python + newconf$python <- input$sys_which_python + newconf$use_gpu <- input$sys_use_gpu + + if (input$sys_use_spacy) { + sptest <- system(paste0( + newconf$python, " -m spacy info ", input$sys_use_spacy_model), intern = TRUE) + if (length(sptest) == 0) { + values$save_config_msg <- paste( + "Error loading spacy, check that it is installed in the specified python env") + return(NULL) + } + if (length(attr(sptest, "status"))) { + values$save_config_msg <- paste( + "Error loading spacy model, check that it has been downloaded") + return(NULL) + } + newconf$use_spacy_model <- input$sys_use_spacy_model + } + newconf$use_spacy <- input$sys_use_spacy + + if (input$sys_use_ft) { + if (!file.exists(input$sys_use_ft_model)) { + values$save_config_msg <- paste( + "Error loading fasttext model, check the specified path") + return(NULL) + } + newconf$use_ft_model <- input$sys_use_ft_model + } + newconf$use_ft <- input$sys_use_ft + + if (input$sys_use_sb) + newconf$use_sb_model <- input$sys_use_sb_model + newconf$use_sb <- input$sys_use_sb + + # if (newconf$use_spacy) if (newconf$use_spacy_model != values$conf$use_spacy_model) { + if (newconf$use_spacy) if (!identical(newconf$use_spacy_model, values$conf$use_spacy_model)) { + file.remove(paste0(values$conf$datadir, values$conf$dataname, "_spa.feather")) + file.remove(paste0(values$conf$datadir, values$conf$dataname, "_ft.feather")) + } + # if (newconf$use_ft) if (newconf$use_ft_model != values$conf$use_ft_model) + if (newconf$use_ft) if (!identical(newconf$use_ft_model, values$conf$use_ft_model)) + file.remove(paste0(values$conf$datadir, values$conf$dataname, "_ft.feather")) + # if (newconf$use_sb) if (newconf$use_sb_model != values$conf$use_sb_model) + if (newconf$use_sb) if (!identical(newconf$use_sb_model, values$conf$use_sb_model)) + file.remove(paste0(values$conf$datadir, values$conf$dataname, "_sb.feather")) + + writeLines(RJSONIO::toJSON(newconf), + paste0("tigger_", newconf$projectid, ".json")) + values$conf <- newconf + initDataTrigger(initDataTrigger() + 1) + removeModal() + }) + + ############################################################################## + ## Reactives : data + ############################################################################## + + ok.commentCol <- reactive(values$conf$commcol) + + ## Current imported data (async) + observeEvent(input$dataImport, { + if (ok.data.running()) return(NULL) + ## Save nrows to conf file on change + if (!is.numeric(input$dataNrows) | !is.numeric(input$dataSkipRows)) return(NULL) + values$conf$dataNrows <- input$dataNrows + values$conf$dataSkipRows <- input$dataSkipRows + if (!is.null(values$conf$projectid)) + writeLines(RJSONIO::toJSON(values$conf), paste0( + "tigger_", values$conf$projectid, ".json")) + initDataTrigger(initDataTrigger() + 1) + }) + + + ## Read data event + observeEvent(initDataTrigger(), { + if (is.null(values$conf)) { + ok.data(NULL) + return(NULL) + } + if (initDataTrigger() == 0) return(NULL) + if (verbose) + cat("DEBUG enter ok.data\n") + if (verbose) + cat("DEBUG enter ok.data trigger", initDataTrigger(), "\n") + ok.data.running(TRUE) + + ## Prepare variables for async launch + coco <- values$conf + coco$python_ok <- values$python_ok + + da_token <- session$token + data_status <- paste0("tigger_", da_token, "_data") + + file_orig_colnames <- paste0(data_status, "_origcols") + file_colstok <- paste0(data_status, "_tok") + file_colsft <- paste0(data_status, "_ft") + file_colssb <- paste0(data_status, "_sb") + file_colsbertpred <- paste0(data_status, "_bertpred") + da_missing <- paste0(data_status, "_bertpred_missing") + + projpath <- paste0(coco$datadir, coco$projectid) + if (!file.exists(projpath)) + dir.create(projpath) + + ## Read data: async launch + data_async <- future({ + ################# + ## Main data file + + set_status(data_status, "Importing main data file...") + res <- arrow::read_feather( + paste0(coco$datadir, coco$dataname, ".feather")) + if (is.null(coco$dataNrows)) return(NULL) + rowselect <- + intersect(1:nrow(res), (1:(coco$dataNrows)) + coco$dataSkipRows) + if (!length(rowselect)) return(NULL) + res <- res[rowselect, , drop = FALSE] + + writeLines(colnames(res), file_orig_colnames) + + + ################# + ## Tokenized text + + ## Import data : feather of two cols, one the id, other the tokenized + set_status(data_status, "Importing tokenized text...") + file_tok <- paste0(coco$datadir, coco$dataname, "_spa.feather") + if (file.exists(file_tok)) { + tokdat <- arrow::read_feather(file_tok) + } else + tokdat <- NULL + tok_remaining <- res[[coco$idcol]] + tok_remaining <- + tok_remaining[! tok_remaining %in% tokdat[[coco$idcol]]] + + ## Tokenize remaining texts + if (length(tok_remaining)) { + if (!coco$python_ok | !coco$use_spacy) { + ## If remaining texts to tokenize but no python, fall back on + ## untokenized text + ## TODO here use quanteda tokenizer with options? + tokdat <- data.frame( + res[[coco$idcol]], + do.call(paste, c(res[match(tok_remaining, res[[coco$idcol]]), + coco$textcols, drop = FALSE], sep = "\n\n"))) + colnames(tokdat) <- c(coco$idcol, "text_spa") + set_status(data_status, "Importing tokenized text: no spacy ok") + } else { + ## If python present, tokenize with spacy + set_status(data_status, + paste("Tokenizing", length(tok_remaining), "new texts")) + tmpfile <- paste0("tigger_tok_", da_token) + arrow::write_feather( + data.frame( + id = tok_remaining, + text = do.call( + paste, c(res[match(tok_remaining, res[[coco$idcol]]), + coco$textcols, drop = FALSE], sep = "\n\n"))), + paste0(tmpfile, ".feather") + ) + system(paste0( + coco$python, " tokenize_spacy.py -d ", tmpfile, ".feather", + " -m ", coco$use_spacy_model)) + tok_new <- arrow::read_feather(paste0(tmpfile, "_spa.feather")) + colnames(tok_new)[colnames(tok_new) == "id"] <- coco$idcol + tokdat <- rbind(tokdat, tok_new) + arrow::write_feather(tokdat, file_tok) + system(paste0("rm ", tmpfile, "*")) + set_status(data_status, "Importing tokenized text: spacy ok") + } + } + + ## Save colname for tokenized text + tmp_cols_tok <- colnames(tokdat)[colnames(tokdat) != coco$idcol] + writeLines(tmp_cols_tok, file_colstok) + + set_status(data_status, "Importing tokenized text: wrote colnames") + + ## Merge with current data + res <- merge(res, tokdat, by = coco$idcol, + all.y = FALSE, sort = FALSE) + rm(tokdat) + + set_status(data_status, "Importing tokenized text: merged") + + ################# + ## FastText embeddings + + set_status(data_status, paste("Loading fastText embeddings")) + + ## FastText embeddings : feather file with id and embeddings + file_ft <- paste0(coco$datadir, coco$dataname, "_ft.feather") + if (file.exists(file_ft)) { + ftdat <- arrow::read_feather(file_ft) + } else + ftdat <- NULL + ft_remaining <- res[[coco$idcol]] + ft_remaining <- + ft_remaining[! ft_remaining %in% ftdat[[coco$idcol]]] + + ## Embed remaining texts, if FT in use + if (length(ft_remaining)) { + if (!coco$python_ok | !coco$use_ft) { + ## If no python or FT, abort if not all rows already embedded + ftdat <- NULL + } else { + set_status(data_status, paste( + "Embedding", length(ft_remaining), "new texts with fastText")) + tmpfile <- paste0("tigger_ft_", da_token) + arrow::write_feather( + data.frame( + id = ft_remaining, + text = do.call( + paste, res[match(ft_remaining, res[[coco$idcol]]), + tmp_cols_tok, drop = FALSE])), + paste0(tmpfile, ".feather") + ) + system(paste0( + coco$python, " embed_fasttext.py -d ", tmpfile, ".feather", + " -m ", coco$use_ft_model)) + ft_new <- arrow::read_feather(paste0(tmpfile, "_ft.feather")) + colnames(ft_new)[colnames(ft_new) == "id"] <- coco$idcol + ftdat <- rbind(ftdat, ft_new) + arrow::write_feather(ftdat, file_ft) + system(paste0("rm ", tmpfile, "*")) + } + } + + if (!is.null(ftdat)) { + ## Merge with current data + writeLines(colnames(ftdat)[colnames(ftdat) != coco$idcol], file_colsft) + res <- merge(res, ftdat, by = coco$idcol, all.y = FALSE, sort = FALSE) + rm(ftdat) + } + + + ################# + ## SBERT embeddings + + set_status(data_status, "Loading SBERT embeddings") + + ## SBERT file : feather of id and embeddings + file_sb <- paste0(coco$datadir, coco$dataname, "_sb.feather") + if (file.exists(file_sb)) { + sbdat <- arrow::read_feather(file_sb) + }else + sbdat <- NULL + sb_remaining <- res[[coco$idcol]] + sb_remaining <- + sb_remaining[! sb_remaining %in% sbdat[[coco$idcol]]] + + ## Embed remaining texts + if (length(sb_remaining)) { + if (!coco$python_ok | !coco$use_sb) { + ## If no python or SB, abort if not all rows already embedded + sbdat <- NULL + } else { + set_status(data_status, paste( + "Embedding", length(sb_remaining), "new texts with SBERT")) + tmpfile <- paste0("tigger_sb_", da_token) + arrow::write_feather( + data.frame( + id = sb_remaining, + text = do.call( + paste, res[match(sb_remaining, res[[coco$idcol]]), + coco$textcols, drop = FALSE])), + paste0(tmpfile, ".feather") + ) + system(paste0( + coco$python, " embed_sbert.py -d ", tmpfile, ".feather", + " -m ", coco$use_sb_model)) + sb_new <- arrow::read_feather(paste0(tmpfile, "_sb.feather")) + colnames(sb_new)[colnames(sb_new) == "id"] <- coco$idcol + sbdat <- rbind(sbdat, sb_new) + arrow::write_feather(sbdat, file_sb) + system(paste0("rm ", tmpfile, "*")) + } + } + + if (!is.null(sbdat)) { + ## Merge with current data + writeLines(colnames(sbdat)[colnames(sbdat) != coco$idcol], file_colssb) + res <- merge(res, sbdat, by = coco$idcol, all.y = FALSE, sort = FALSE) + rm(sbdat) + } + + ################# + ## BERT predictions + + set_status(data_status, "Loading BERT predictions") + + ## BERT predictions : feathers of id and probs + ## TODO: take full prediction if existing + + files_pred <- dir( + coco$datadir, paste0("^", coco$dataname, "_bertpred_.*[.]feather$")) + # files_predall <- dir( + # coco$datadir, paste0("^", coco$dataname, "_bertpredall_.*[.]feather$")) + # files_pred <- files_pred[ + # ! gsub("_bertpred_", "_bertpredall_", files_pred) %in% files_predall] + # files_pred <- c(files_predall, files_pred) + + tmp_bertpred_cols <- list() + if (length(files_pred)) { + for (ipred in files_pred) { + preddat <- arrow::read_feather(paste0(coco$datadir, ipred)) + predname <- + gsub(paste0("^", coco$dataname, "_bertpred_(.*)[.]feather$"), + "\\1", ipred) + # predname <- + # gsub(paste0("^", coco$dataname, "_bertpred(all)?_(.*)[.]feather$"), + # "\\2", ipred, perl = TRUE) + n_missing <- sum(! res[[coco$idcol]] %in% preddat[[coco$idcol]]) + if (n_missing) { + write( + paste0("BERT prediction ", predname, " : ", n_missing, + " texts missing in prediction."), + da_missing, append = TRUE) + } else { + colnames(preddat) <- gsub( + "^bertpred_", paste0("bertpred_", predname), colnames(preddat)) + tmp_bertpred_cols[[predname]] <- + colnames(preddat)[colnames(preddat) != coco$idcol] + res <- merge(res, preddat, by = coco$idcol, + all.y = FALSE, sort = FALSE) + } + rm(preddat) + } + if (length(tmp_bertpred_cols)) { + writeLines(names(tmp_bertpred_cols), file_colsbertpred) + lapply(names(tmp_bertpred_cols), function(x) { + writeLines(tmp_bertpred_cols[[x]], paste0(file_colsbertpred, "_", x)) + }) + } + } + + ## Comment col + set_status(data_status, "Loading comments") + tmpfile <- paste0(projpath, "/", coco$commcol, ".csv") + if (file.exists(tmpfile)) { + tmpcol <- read.csv(tmpfile) + colnames(tmpcol)[2] <- coco$commcol + ## Remove duplicates, keep newest + tmpcol <- tmpcol[nrow(tmpcol):1, , drop = FALSE] + tmpcol <- tmpcol[!duplicated(tmpcol[, 1]), , drop = FALSE] + tmpcol <- tmpcol[nrow(tmpcol):1, , drop = FALSE] + res <- merge(res, tmpcol, by = coco$idcol, + all.x = TRUE, all.y = FALSE, sort = FALSE) + res[[coco$commcol]][is.na(res[[coco$commcol]])] <- "" + } else + res[[coco$commcol]] <- "" + + # ## Tagging cols + # set_status(data_status, "Loading tags") + # for (itag in coco$tagcols) { + # tmpfile <- paste0(projpath, "/", itag, ".csv") + # if (file.exists(tmpfile)) { + # tmpcol <- read.csv(tmpfile) + # tmpcol[[paste0("hist_", itag)]] <- 1:nrow(tmpcol) + # ## Remove duplicates, keep newest + # tmpcol <- tmpcol[nrow(tmpcol):1, ] + # tmpcol <- tmpcol[!duplicated(tmpcol[, 1]), ] + # tmpcol <- tmpcol[nrow(tmpcol):1, ] + # if (verbose) + # cat("DEBUG imported ", itag, nrow(tmpcol), "\n") + # res <- merge(res, tmpcol, by = coco$idcol, + # all.x = TRUE, all.y = FALSE, sort = FALSE) + # } else { + # res[[itag]] <- NA + # res[[paste0("hist_", itag)]] <- NA + # } + # } + ## Tagging cols + set_status(data_status, "Loading tags") + for (itag in coco$tagcols) { + itaghist <- paste0("hist_", itag) + if (is.null(res[[itag]]) | all(is.na(res[[itag]]))) { + res[[itag]] <- NA + res[[itaghist]] <- NA + } else { + res[[itaghist]][!is.na(res[[itag]])] <- 1:sum(!is.na(res[[itag]])) + } + tmpfile <- paste0(projpath, "/", itag, ".csv") + if (file.exists(tmpfile)) { + tmpcol <- read.csv(tmpfile) + tmpcol[[itaghist]] <- 1:nrow(tmpcol) + + max(0, suppressWarnings(max(na.omit(res[[itaghist]])))) + ## Remove duplicates, keep newest + tmpcol <- tmpcol[nrow(tmpcol):1, ] + tmpcol <- tmpcol[!duplicated(tmpcol[, 1]), ] + tmpcol <- tmpcol[nrow(tmpcol):1, ] + tmpcol <- tmpcol[tmpcol[[coco$idcol]] %in% res[[coco$idcol]], ] + if (verbose) + cat("DEBUG imported", itag, nrow(tmpcol), "\n") + # res <- merge(res, tmpcol, by = coco$idcol, + # all.x = TRUE, all.y = FALSE, sort = FALSE) + if (nrow(tmpcol)) { + idmatches <- match(tmpcol[[coco$idcol]], res[[coco$idcol]]) + # idmatches <- idmatches[!is.na(idmatches)] + res[[itag]][idmatches] <- tmpcol[[itag]] + res[[itaghist]][idmatches] <- tmpcol[[itaghist]] + } + } + } + + set_status(data_status, "Finalizing data import") + res + }) %...>% ok.data.async() + data_async <- catch(data_async, function(error) { + values$modelDiagno <- paste0("Error in data import: ", error) + }) + data_async <- finally(data_async, function() { + ################# + ## Finalize data importation + res <- ok.data.async() + if (is.null(res)) return(NULL) + + set_status(data_status, "Finalizing data import: read columns") + ## Read column (tok, ft, sb, ...) names from dedicated files + values$tagcols <- coco$tagcols + values$cols_tok <- readLines(file_colstok) + values$cols_ft <- NULL + if (file.exists(file_colsft)) { + values$cols_ft <- readLines(file_colsft) + } + values$cols_sb <- NULL + if (file.exists(file_colssb)) { + values$cols_sb <- readLines(file_colssb) + } + set_status(data_status, "Finalizing data import: bertpred") + if (file.exists(file_colsbertpred)) { + tmp_bertpred <- readLines(file_colsbertpred) + values$cols_bertpred <- lapply(tmp_bertpred, function(x) + readLines(paste0(file_colsbertpred, "_", x))) + names(values$cols_bertpred) <- tmp_bertpred + } else + values$cols_bertpred <- NULL + set_status(data_status, "Finalizing data import: bertpred missing") + if (file.exists(da_missing)) { + lapply(readLines(da_missing), function(x) + showNotification(paste( + x, "\n", + "You may predict on current text in BERT panel, ", + "and then reload the data."))) + } + + ## Populate regressors list in prediction panel + set_status(data_status, "Finalizing data import: populating regressors") + regr_choices <- c( + "Regex" = "regex", + "Word counts (DFM)" = "dfm") + if (!is.null(values$cols_ft)) + regr_choices <- c(regr_choices, "Word embeddings (FastText)" = "ft") + if (!is.null(values$cols_sb)) + regr_choices <- c(regr_choices, "Sentence embeddings (SBERT)" = "sb") + regr_choices <- c(regr_choices, "Extra predictors" = "extra") + + if (length(values$cols_bertpred)) { + tmp_choices <- paste0("bertpred_", names(values$cols_bertpred)) + names(tmp_choices) <- paste0("BERT_", names(values$cols_bertpred)) + regr_choices <- c(regr_choices, tmp_choices) + } + + # if (length(regr_choices) > 3) { + regr_choices_sel <- + regr_choices[regr_choices %in% c("regex", "ft", "sb")] + # } else + # regr_choices_sel <- c("regex", "dfm") + + updateSelectizeInput( + session, "use_regressors", choices = regr_choices, + selected = regr_choices_sel) + values$new_use_regressors <- regr_choices_sel + + othercols <- colnames(res) + othercols <- othercols[! othercols %in% c( + values$cols_tok, values$cols_ft, values$cols_sb, values$cols_bertpred, + values$tagcols, paste0("hist_", values$tagcols), + coco$commcol, coco$textcols)] + updateSelectInput( + session, "dlTagSelect", + choices = c("tags", "comments", "predictions", othercols), + selected = c("tags", "comments", "predictions")) + + ## Populate extra regressors lists in prediction panel + updateSelectizeInput( + session, "use_ootregnum", choices = othercols, selected = NULL) + updateSelectizeInput( + session, "use_ootregcat", choices = othercols, selected = NULL) + + ## Project path + values$projectdir <- paste0(projpath, "/") + + ## Create tagmat: data.frame of all current taggings + values$tagmat <- res[, coco$tagcols, drop = FALSE] + ## Create tagmathist: data.frame of historical order of taggings + values$tagmathist <- res[, paste0("hist_", coco$tagcols), drop = FALSE] + colnames(values$tagmathist) <- coco$tagcols + + ## Save data original colnames + values$data_orig_colnames <- readLines(file_orig_colnames) + + ok.data(ok.data.async()) + ok.data.async(NULL) + ok.data.running(FALSE) + set_status(data_status, "Exit data async") + system(paste0("rm ", data_status, "*")) + values$modelDiagno <- "No model" + + + if(trainTrigger() == 0) trainTrigger(1) ## For first run. TODO set trainTrigger to 0 when changing data? + if (verbose) cat("DEBUG Exit data async finally\n") + }) + NULL + }) + + ## Read data import status from file + observe({ + if (ok.data.running()) { + invalidateLater(100) + tmpfile <- paste0("tigger_", session$token, "_data") + if (file.exists(tmpfile)) { + values$modelDiagno <- paste(collapse = "\n", scan( + tmpfile, what = "character", sep="\n", quiet = TRUE)) + } else + values$modelDiagno <- "" + } + }) + + + ############### + ## ok.text() reactive + ############### + ok.text <- eventReactive(ok.data(), { + if (is.null(ok.data())) return(NULL) + res <- ok.data()[, values$cols_tok[1]] + if (length(values$cols_tok) > 1) { + for (icol in values$cols_tok[-1]) { + res <- paste0(res, "\n\n", ok.data()[, icol]) + } + } + # queryTrigger(queryTrigger() + 1) + if (verbose) + cat("DEBUG ok.text", length(res), "\n") + res + }) + + ok.labelcol <- reactive({ + if (is.null(ok.data())) return(NULL) + if (verbose) + # cat("DEBUG enter ok.labelcol\n") + if (is.null(input$selectScheme)) return(values$conf$tagcols[1]) + input$selectScheme + }) + + + ok.nicetext <- reactive({ + req(ok.data(), values$conf$textcols) + # if (is.null(ok.data())) return(NULL) + do.call(paste, c(ok.data()[, values$conf$textcols, drop = FALSE], + sep = "\n\n")) + }) + + ok.regressor.names <- reactive({ + daregs <- values$new_use_regressors + if (verbose) + cat("DEBUG enter ok.regressor.names ", paste(daregs, collapse = " "), "\n") + res <- NULL + if ("ft" %in% daregs) + res <- c(res, values$cols_ft) + if ("sb" %in% daregs) + res <- c(res, values$cols_sb) + if (length(values$cols_bertpred)) { + for (ipred in 1:length(values$cols_bertpred)) { + if (paste0("bertpred_", names(values$cols_bertpred)[ipred]) %in% daregs) + res <- c(res, values$cols_bertpred[[ipred]]) + } + } + if (verbose) + cat("DEBUG exit ok.regressor.names:", length(res), "\n") + res + }) + + ok.contextCol <- reactive({ + if (verbose) + cat("DEBUG enter ok.contextCol\n") + # res <- isolate(values$conf$contextcols) + res <- values$conf$contextcols + names(res) <- res + res + }) + + ## Initiate label and queries history, on ok.text() or labelcol change, + observeEvent(list(ok.text(), ok.labelcol()), { + if (verbose) + cat("DEBUG initiate label\n") + if (is.null(ok.labelcol())) { + values$label <- NULL + values$labelhist <- NULL + values$queries <- NULL + values$comment <- NULL + values$wasTagged <- NULL + values$wasRetagged <- NULL + } else { + values$label <- values$tagmat[, ok.labelcol()] + values$labelhist <- values$tagmathist[, ok.labelcol()] + values$queries <- which(!is.na(values$label)) + values$comment <- ok.data()[[values$conf$commcol]] + values$wasTagged <- !is.na(values$label) + values$wasRetagged <- rep(FALSE, length(values$label)) + } + }) + + ## On values$label change, values$tagmat is updated for separate history + observeEvent(values$label, { + if (is.null(values$label)) return(NULL) + if (verbose) + cat("DEBUG Update tagmat from label\n") + values$tagmat[, ok.labelcol()] <- values$label + values$tagmathist[, ok.labelcol()] <- values$labelhist + }) + + + ############################################################################## + ## Reactives : training + ############################################################################## + + ## Message of how many tagged and untagged, for model diagnostics box + ok.msgtagged <- reactive({ + if (verbose) + cat("DEBUG enter ok.msgtagged\n") + # if (is.null(ok.data()) | ok.data.running()) { + if (is.null(ok.data())) { + return("No data yet ") + } + + tmp <- "" + # if (sum(is.na(values$label)) == 0) + # tmp <- paste0(tmp, "Data correction mode (0 NA labels left).\n") + paste0(tmp, "Tagged: ", sum(!is.na(values$label)), + " ; Untagged: ", sum(is.na(values$label))) + }) + + ## Training possible? At least 3 texts for 2 tags + ok.train.possible <- reactive({ + if (verbose) + cat("DEBUG enter ok.train.possible\n") + labtab <- table(values$label) + if (length(labtab) < 2) return(FALSE) + if (sum(labtab > 2) < 2) return(FALSE) + TRUE + }) + + ## Compute DFM (async) + ## TODO make this less reactive + observe({ + # if ("dfm" %in% input$use_regressors & !values$dfmGo) + if ("dfm" %in% values$new_use_regressors & !values$dfmGo) + values$dfmGo <- TRUE + }) + ok.dfm <- reactiveVal() + observe({ + if (is.null(ok.text())) return(NULL) + if (! values$dfmGo) return(NULL) + if (!is.numeric(input$dfmNgrams)) return(NULL) + if (verbose) + cat("DEBUG enter dfm observer\n") + + datext <- ok.text() + dfmNgrams <- input$dfmNgrams + dfmTfIdf <- input$dfmTfIdf + dfmMinDocfreq <- input$dfmMinDocfreq + dfmMinTermfreq <- input$dfmMinTermfreq + dfmTfScheme <- input$dfmTfScheme + dfmDfScheme <- input$dfmDfScheme + + dfm_async <- future(seed = TRUE, { + dadfm <- datext %>% + tolower() %>% tokens(remove_punct = F) %>% + # tokens_select(pattern = stopwords(language= "fr"), selection = "remove") %>% + tokens_ngrams(n= 1:dfmNgrams) %>% dfm() %>% + dfm_trim(min_docfreq = dfmMinDocfreq, min_termfreq = dfmMinTermfreq, + verbose = TRUE) + if (dfmTfIdf) { + dadfm <- dadfm %>% + dfm_tfidf(scheme_tf = dfmTfScheme, scheme_df = dfmDfScheme) + } + suppressWarnings({ # TODO : check + dadfm <- quanteda::convert(dadfm, to= "tm") + }) + dadfm <- Matrix::sparseMatrix(i= dadfm$i, j= dadfm$j, x= dadfm$v, + dimnames = dadfm$dimnames) + dadfm + }) %...>% ok.dfm() + dfm_async <- catch(dfm_async, function(error) { + values$modelDiagno <- paste0("Error in DFM: ", error) + }) + }) + + ## Predictor : df or matrix of dfm and extra + ok.regressor.matrix <- reactive({ + if (is.null(ok.text())) return(NULL) + if (is.null(ok.regressor.names())) return(NULL) + if (verbose) + cat("DEBUG enter ok.regressor.matrix\n") + model.matrix(~., data= ok.data()[, ok.regressor.names()])[, -1] + }) + + ok.extraregressor.matrix <- reactive({ + req(ok.text()) + if (! "extra" %in% values$new_use_regressors) return(NULL) + if (verbose) + cat("DEBUG enter ok.extraregressor.matrix\n") + + res <- NULL + for (inum in input$use_ootregnum) { + suppressWarnings({tmp <- as.numeric(ok.data()[[inum]])}) + if (all(is.na(tmp))) { + showNotification(paste0(inum, " is all NAs or non-numeric ; predictor not used"), + duration = 5, type = "warning") + next + } + tmp[is.na(tmp)] <- median(tmp, na.rm = TRUE) + res <- cbind(res, tmp) + } + for (icat in input$use_ootregcat) { + tmp <- ok.data()[[icat]] + if (! length(unique(tmp)) %in% 2:100) { + showNotification(paste0(icat, " has ", length(unique(tmp)), + " unique values ; predictor not used"), + duration = 5, type = "warning") + next + } + tmp <- as.character(tmp) + tmp[is.na(tmp)] <- "" + tmp <- as.factor(tmp) + tmpmat <- model.matrix(~., data.frame(cat = tmp))[, -1, drop = FALSE] + res <- cbind(res, tmpmat) + } + if (verbose) + cat("DEBUG exit ok.extraregressor.matrix", ncol(res), "\n") + res + }) + + ok.extraRegex.matrix <- reactive({ + if (is.null(ok.nicetext())) return(NULL) + if (is.null(values$extraRegex)) return(NULL) + if (is.null(values$regex_inuse)) return(NULL) + if (!any(values$regex_inuse)) return(NULL) + if (is.null(values$regex_case)) return(NULL) + if (verbose) + cat("DEBUG enter ok.extraRegex.matrix\n") + tmp <- as.matrix(sapply(which(values$regex_inuse), function(iregex) { + 1 * stringi::stri_detect_regex( + ok.nicetext(), values$extraRegex[iregex], + opts_regex = list(case_insensitive = !values$regex_case[iregex])) + })) + colnames(tmp) <- values$extraRegex[values$regex_inuse] + tmp + }) + + ok.predictor <- reactive({ + req(values$new_use_regressors) + if (ok.data.running()) return(NULL) + if (is.null(ok.text())) return(NULL) + if (verbose) + cat("DEBUG enter ok.predictor\n") + dadfm <- NULL + if ("dfm" %in% values$new_use_regressors) { + dadfm <- ok.dfm() + } else { + if (input$predModel == "naive bayes") { + values$modelDiagno <- + "Error in training: Naive Bayes requires DFM word counts as predictors." + return(NULL) + } + } + if (length(ok.regressor.names()) > 0) { + if (input$predModel == "random forest") { + if (is.null(dadfm)) { + dadfm <- ok.data()[, ok.regressor.names()] + } else + dadfm <- cbind(ok.data()[, ok.regressor.names()], as.matrix(dadfm)) + } + if (input$predModel %in% c("lasso", "linear", "knn")) { + dadfm <- cbind(ok.regressor.matrix(), dadfm) + } + } + # if ("regex" %in% input$use_regressors && length(values$regex_inuse) > 0) { + if ("regex" %in% values$new_use_regressors & !is.null(ok.extraRegex.matrix())) { + dadfm <- cbind(dadfm, ok.extraRegex.matrix()) + } + if ("extra" %in% values$new_use_regressors) { + dadfm <- cbind(dadfm, ok.extraregressor.matrix()) + } + + if (is.null(dadfm)) { + values$modelDiagno <- "Error in training: no predictors selected" + if (verbose) + cat("DEBUG ok.predictor exit: no predictors\n") + return(NULL) + } + + if (input$predModel == "linear") + dadfm <- SparseM::as.matrix.csr(dadfm, nrow(dadfm), ncol(dadfm)) + if (input$predModel == "naive bayes") + dadfm <- as.dfm(dadfm) + if (verbose) + cat("DEBUG exit ok.predictor: ", ncol(dadfm), "\n") + dadfm + }) + + + ## Train model async + ok.train <- reactiveVal() + ok.train.async <- reactiveVal() + observeEvent( + list(trainTrigger(), input$modelTrain), + { + if (is.null(ok.predictor())) return(NULL) + if (verbose) + cat("DEBUG enter ok.train observer\n") + if (!ok.train.possible()) { + values$modelDiagno <- + "Tag at least 3 texts for 2 tags to train prediction model." + ok.train(NULL) + # queryTrigger(queryTrigger() + 1) + return(NULL) + } + ready2tag(0) + values$modelDiagno <- paste0("Training ", input$predModel) + + tmp_model <- input$predModel + if (tmp_model == "random forest") { + if (!is.numeric(input$rfNumTrees) | !is.numeric(input$rfMtry) | + !is.numeric(input$rfSampleFrac)) { + ok.train(NULL) + return(NULL) + } + train_fun <- ranger::ranger + train_args <- list( + x= ok.predictor()[!is.na(values$label), ], + y= as.factor(values$label[!is.na(values$label)]), + num.trees = input$rfNumTrees, + mtry = if (input$rfMtry > 0) input$rfMtry, + sample.fraction = input$rfSampleFrac, probability = TRUE) + } else { + if (tmp_model == "linear") { + tmpvalues <- values$label + if (!is.numeric(input$liblinCost)) { + ok.train(NULL) + return(NULL) + } + for (ival in na.omit(unique(tmpvalues))) + if (sum(tmpvalues == ival, na.rm = T) == 1) + tmpvalues[which(tmpvalues == ival)] <- NA + train_fun <- LiblineaR::LiblineaR + train_args <- list( + data= ok.predictor()[!is.na(tmpvalues), ], + target= as.factor(tmpvalues[!is.na(tmpvalues)]), + type= 0, cost = input$liblinCost) + } else if (tmp_model == "lasso") { + tmpvalues <- values$label + for (ival in na.omit(unique(tmpvalues))) + if (sum(tmpvalues == ival, na.rm = T) == 1) + tmpvalues[which(tmpvalues == ival)] <- NA + train_fun <- glmnet::glmnet + train_args <- list( + x= ok.predictor()[!is.na(tmpvalues), ], + y= as.factor(tmpvalues[!is.na(tmpvalues)]), + family= "multinomial") + } else if (tmp_model == "naive bayes") { + if (!is.numeric(input$naiveSmooth)) { + ok.train(NULL) + return(NULL) + } + tmpvalues <- values$label + for (ival in na.omit(unique(tmpvalues))) + if (sum(tmpvalues == ival, na.rm = T) == 1) + tmpvalues[which(tmpvalues == ival)] <- NA + train_fun <- quanteda.textmodels::textmodel_nb + train_args <- list( + x= ok.predictor()[!is.na(tmpvalues), ], + y= as.factor(tmpvalues[!is.na(tmpvalues)]), + smooth = input$naiveSmooth, prior = input$naivePrior, + distribution = input$naiveDistri) + } else if (tmp_model == "knn") { + train_fun <- function(x) return(TRUE) + train_args <- list(x = "knn") + } + } + + train_async <- future(seed = TRUE, { + suppressWarnings(do.call(train_fun, train_args)) + }) %...>% ok.train.async() + train_async <- catch(train_async, function(error) { + values$modelDiagno <- paste0("Model training error: ", error) + ok.train(NULL) + ok.train.async(NULL) + }) + train_async <- finally(train_async, function() { + values$modelDiagno <- "Training done, predicting..." + ok.train(ok.train.async()) + ok.train.async(NULL) + predTrigger(predTrigger() + 1) + }) + NULL + } + ) + + ## Model prediction on full dataset (async) + ok.pred <- reactiveVal() + ok.pred.async <- reactiveVal() + # observeEvent(list(ok.train(), input$glmLambda, input$knnK, values$retagged), { + observeEvent(predTrigger(), { + if (is.null(ok.train())) { + ok.pred(NULL) + return(NULL) + } + if (verbose) + cat("DEBUG ok.pred()\n") + + damodel <- input$predModel + if (input$predModel == "random forest") { + pred_args <- list(ok.train(), data = ok.predictor()) + dacolnames <- colnames(ok.train()$predictions) + } else if (input$predModel == "lasso") { + if (!is.numeric(input$glmLambda)) { + ok.pred(NULL) + return(NULL) + } + pred_args <- list(ok.train(), newx = ok.predictor(), s = input$glmLambda, + type = "response") + } else if (input$predModel == "linear") { + pred_args <- list(ok.train(), newx = ok.predictor(), proba = TRUE) + } else if (input$predModel == "naive bayes") { + pred_args <- list(ok.train(), newdata = ok.predictor(), + type = "probability") + } else if (input$predModel == "knn") { + if (!is.numeric(input$knnK)) { + ok.pred(NULL) + return(NULL) + } + pred_args <- list(train = ok.predictor()[!is.na(values$label), ], + test = ok.predictor(), + cl = values$label[!is.na(values$label)], + k = input$knnK, prob = TRUE) + } + + pred_async <- future(seed = TRUE, { + if (damodel == "random forest") { + res <- do.call(predict, pred_args)$predictions + colnames(res) <- dacolnames + res + } else if (damodel == "lasso") { + do.call(predict, pred_args)[, , 1] + } else if (damodel == "linear") { + do.call(predict, pred_args)$probabilities + } else if (damodel == "naive bayes") { + do.call(predict, pred_args) + } else if (damodel == "knn") { + tmp <- do.call(class::knn, pred_args) + ## A bit flaky here: probs of non-best classes uniformly share the rest, + ## and distance is euc + tmpvalues <- sort(unique(pred_args$cl)) + res <- matrix(0, nrow(pred_args$test), length(tmpvalues), + dimnames = list(NULL, tmpvalues)) + res[cbind(1:nrow(res), tmp)] <- attr(tmp, "prob") - + ((1 - attr(tmp, "prob")) / (ncol(res) - 1)) + res <- res + (1 - attr(tmp, "prob")) / (ncol(res) - 1) + res + } + }) %...>% ok.pred.async() + pred_async <- catch(pred_async, function(error) { + values$modelDiagno <- paste0("Error in prediction: ", error) + }) + pred_async <- finally(pred_async, function() { + if (verbose) + cat("DEBUG ok.pred() out of async\n") + ok.pred(ok.pred.async()) + ok.pred.async(NULL) + # predlabTrigger(predlabTrigger() + 1) + diagTrigger(diagTrigger() + 1) + queryNext(queryNext() + 1) + }) + }) + + ## Best predicted label for full dataset + # ok.predlab1 <- eventReactive(predlabTrigger(), { + # if (is.null(ok.pred())) return(NULL) + # if (verbose) + # cat("DEBUG ok.predlab1\n") + # # queryTrigger(queryTrigger() + 1) + # colnames(ok.pred())[max.col(ok.pred())] + # }) + ok.predlab1 <- reactive({ + if (is.null(ok.pred())) return(NULL) + if (verbose) + cat("DEBUG ok.predlab1\n") + colnames(ok.pred())[max.col(ok.pred())] + }) + + ## On scheme change, forget existing model, queue, query and hist + ## load scheme description and predictor-regex + observeEvent(ok.labelcol(), { + if (verbose) + cat("DEBUG event selectScheme", ok.labelcol(), "\n") + ok.pred(NULL) + ok.train(NULL) + values$queryqueue <- NULL + values$modelDiagno <- "No model" + values$histSaveStack <- NULL + + ## Load description file + descr <- "" + if (file.exists(paste0(values$conf$datadir, values$conf$projectid, + "/", input$selectScheme, ".txt"))) { + descr <- paste(collapse = "\n", readLines(paste0( + values$conf$datadir, values$conf$projectid, + "/", input$selectScheme, ".txt"))) + } + updateTextAreaInput(session, "schemeDescr", value = descr) + + ## Load regex from json + regexfile <- paste0(values$projectdir, input$selectScheme, "_regex.json") + if (file.exists(regexfile)) { + if (verbose) + cat("DEBUG enter regex.json\n") + tmp <- RJSONIO::fromJSON(paste(collapse = "\n", readLines(regexfile))) + if (is.null(tmp$regex)) return(NULL) + values$extraRegex <- tmp$regex + names(values$extraRegex) <- sapply(tmp$regex, label2hash) + for (iregex in 1:length(tmp$regex)) { + values[[paste0("useregex_", names(values$extraRegex)[iregex])]] <- + tmp$use[iregex] + values[[paste0("caseregex_", names(values$extraRegex)[iregex])]] <- + tmp$case[iregex] + } + } else + values$extraRegex <- NULL + + }) + + + + ## queryNext event: update values$newQuery ; called by queryNext(), or by + ## queryQueue or regex change + ## Requires : + ## - queryQueue (reactive: indices of current queue), and + ## - wasTagged (boolean vector indicating whether each element has been tagged this session, used in "on untagged") + ## - wasRetagged (boolean vector indicating whether each element has been tagged this session, used in "on tagged") + ## TODO: make a scheme-list of wasTagged, now it will be wiped at each scheme change? + observeEvent( + list(queryNext(), values$queryQueue, + input$sampleChoice, input$taggedWhich, input$maxprobWhich, + input$regexFilter, input$regexCaseSens, + input$visuLock, values$visuZoom$xlim, values$visuZoom$ylim), + { + if (is.null(values$queryQueue)) return(NULL) + if (verbose) + cat("DEBUG queryNext\n") + + queue <- values$queryQueue + + if (input$sampleChoice == "untagged") { + queue <- queue[!values$wasTagged[queue]] + } else if (input$sampleChoice == "tagged") { + queue <- queue[!is.na(values$label)[queue] & !values$wasRetagged[queue]] + if (input$taggedWhich != "all") { + queue <- queue[values$label[queue] == input$taggedWhich] + } + } else if (input$sampleChoice == "all") { + queue <- queue[!values$wasRetagged[queue]] + } + + if (input$visuLock) if (!is.null(values$visuZoom$xlim)) { + if (!is.null(ok.visu())) + queue <- queue[ok.visu()[queue, 1] >= values$visuZoom$xlim[1] & + ok.visu()[queue, 1] <= values$visuZoom$xlim[2] & + ok.visu()[queue, 2] >= values$visuZoom$ylim[1] & + ok.visu()[queue, 2] <= values$visuZoom$ylim[2]] + } + + if (length(queue)) if (nchar(input$regexFilter)) { + try_regex <- try(silent = TRUE, { + queue <- queue[ + stringi::stri_detect_regex( + ok.nicetext()[queue], input$regexFilter, + opts_regex = list(case_insensitive = !input$regexCaseSens))] + }) + if (inherits(try_regex, "try-error")) { + showNotification("Invalid regex", duration = 2, type = "error") + values$newQuery <- NULL + return(NULL) + } + } + + if (!length(queue)) { + if (nchar(input$regexFilter)) { + showNotification("No texts matching the regex", + duration = 2, type = "warning") + } else + showNotification("No more texts to be tagged in this set", + duration = 2, type = "warning") + values$newQuery <- NULL + return(NULL) + } + + values$lastQuery <- queue[1] # ?? + values$newQuery <- queue[1] + ready2tag(1) + } + ) + + ## queryQueue event: fix values$queryQueue, (indices of current queue) + observeEvent( + list(ok.data(), ok.pred(), input$strategy, input$maxprobWhich, + input$selectProject, input$selectScheme), { + if (input$strategy %in% c("entropy", "maxprob") & is.null(ok.pred())) { + values$queryQueue <- NULL + values$lastQuery <- NULL + values$newQuery <- NULL + return(NULL) + } + if (is.null(ok.text())) return(NULL) + if (verbose) + cat("DEBUG queryQueue change\n") + + if (input$strategy == "sequential") { + values$queryQueue <- 1:length(ok.text()) + } else if (input$strategy == "random") { + values$queryQueue <- sample(length(ok.text())) + } else { + dapred <- ok.pred() + if (input$strategy == "entropy") { + tominimize <- rowSums(dapred * log(dapred)) + if (any(dapred == 0)) { + tominimize[dapred == 0] <- 0 + } + } + if (input$strategy == "maxprob") { + if (! input$maxprobWhich %in% colnames(dapred)) return(NULL) + tominimize <- -dapred[, input$maxprobWhich]; + } + values$queryQueue <- order(tominimize) + } + } + ) + + + ## Update uniqueLabels and uniqueLabelsAll if necessary on values$label change + observeEvent(values$label, { + if (is.null(values$label)) return(NULL) + if (verbose) + cat("DEBUG uniqueLabels\n") + dalabs <- sort(na.omit(unique(values$label))) + # if (!length(dalabs)) return(NULL) + if (!identical(dalabs, values$uniqueLabels)) + values$uniqueLabels <- dalabs + if (any(! dalabs %in% values$uniqueLabelsAll)) + values$uniqueLabelsAll <- c(values$uniqueLabelsAll, + dalabs[! dalabs %in% values$uniqueLabelsAll]) + }) + + + + ############################################################################## + ## Project management + ############################################################################## + + output$cpDataMessage <- renderUI({ + req(values$cpDataMessage) + p(values$cpDataMessage) + }) + + output$cpConfirmMessage <- renderUI({ + req(values$cpConfirmMessage) + p(strong(paste0("Error: ", values$cpConfirmMessage))) + }) + + output$cpIdcolUI <- renderUI({ + req(values$cpNewData) + if (!is.data.frame(values$cpNewData)) return(NULL) + selectInput("cpIdcol", NULL, colnames(values$cpNewData)) + }) + + output$cpTextcolUI <- renderUI({ + req(values$cpNewData) + if (!is.data.frame(values$cpNewData)) return(NULL) + selectInput("cpTextcol", NULL, colnames(values$cpNewData), multiple = TRUE) + }) + + output$cpContextcolUI <- renderUI({ + req(values$cpNewData) + if (!is.data.frame(values$cpNewData)) return(NULL) + selectInput("cpContextcol", NULL, colnames(values$cpNewData), multiple = TRUE) + }) + + output$cpTagcolUI <- renderUI({ + req(values$cpNewData) + if (!is.data.frame(values$cpNewData)) return(NULL) + selectInput("cpTagcol", NULL, colnames(values$cpNewData), multiple = TRUE) + }) + + output$cpCommcolUI <- renderUI({ + req(values$cpNewData) + if (!is.data.frame(values$cpNewData)) return(NULL) + selectInput("cpCommcol", NULL, c("none", colnames(values$cpNewData))) + }) + + output$cp_spacyUI <- renderUI( + textInput("cp_use_spacy_model", NULL, + modelnames$spacy_name[modelnames$short == input$cp_lang], + placeholder = "(spacy model name)") + ) + + output$cp_spacyDlUI <- renderUI(HTML(paste( + "Model must be downloaded once, from python:
    ", + input$cp_which_python, "-m spacy download", + modelnames$spacy_name[modelnames$short == input$cp_lang], "

    " + ))) + + output$cp_ftUI <- renderUI( + textInput("cp_use_ft_model", NULL, + modelnames$fasttext_name[modelnames$short == input$cp_lang], + placeholder = "(fasttext model path)") + ) + + output$cp_ftDlUI <- renderUI(list(HTML(paste( + "Model can be downloaded here")), + br(), br())) + + output$cp_sbUI <- renderUI( + textInput("cp_use_sb_model", NULL, ifelse( + input$cp_lang %in% c("ar", "zh", "nl", "en", "fr", "de", + "it", "ko", "pl", "pt", "ru", "es", "tr"), + "distiluse-base-multilingual-cased-v1", + "distiluse-base-multilingual-cased-v2"), + placeholder = "(custom sentence_transformers model)") + ) + + + + ## Create new project, with modal dialog + observeEvent(input$createProject, { + if (verbose) + cat("DEBUG create project\n") + showModal(modalDialog( + title = "New project", + + p("Create a new text tagging project."), + p("This mainly involves choosing a dataset, and how to process it."), + p("Items marked by a star ", strong("*"), + " cannot be changed once the project is created."), + + hr(), + h4("Files and folders"), + + fluidRow( + column(3, p(strong("*Project name"))), + column(5, textInput("cpName", NULL, placeholder = "(Required)")), + column(4, p("Tags will be stored in a new directory by this name.")) + ), + fluidRow( + column(3, p(strong("*Data directory"))), + column(5, textInput("cpDatadir", NULL, placeholder = "(Required)")), + column(4, p("Place (on the server) where the data and project are stored")) + ), + fluidRow( + column(3, p(strong("*Data filename"))), + column(5, checkboxInput("cpDoImport", "New data", TRUE)), + column(4, p("Main file, containing id and text columns")) + ), + + conditionalPanel("!input.cpDoImport", list( + ## Existing data + fluidRow( + column(3, p(strong("Existing file"))), + column(5, textInput("cpDatafile", NULL, placeholder = "(Required)")), + column(4, actionButton("cpDatafileGo", "Import")) + ) + )), + conditionalPanel("input.cpDoImport", { + ## Import data + wellPanel( + selectInput('cpNDfiletype', NULL, + choices = c("Text file (.csv, .txt)" = "csv_txt", + "Feather" = "feather", + "Parquet" = "parquet", + "OpenDocument (.ods)" = "ods", + "Microsoft Excel (.xls, .xlsx)" = "excel", + "SPSS (.sav, .por)" = "spss", + "SAS (.sas7bdat)" = "sas_data", + "Stata v5-v12 (.dta)" = "stata"), + selected = "csv_txt" + ), + fileInput('cpNDfile', 'Choose File'), + + checkboxInput("cpNDparams", "File parameters"), + + conditionalPanel( + "input.cpNDparams", + conditionalPanel( + "input.cpNDfiletype == 'csv_txt'", + + fluidRow(column(4, p('Header')), + column(8, selectInput('cpCSVheader', NULL, + c("Header" = TRUE, + "No Header" = FALSE), + "Auto"))), + fluidRow(column(4, p('Separator')), + column(8, selectInput('cpCSVsep', NULL, + c("Comma ','" = ",", + "Semicolon ';'" = ";", + "Tab" = "\t", + "Space" = " "), + "Auto"))), + fluidRow(column(4, p('Quote')), + column(8, selectInput('cpCSVquote', NULL, + c("Double Quote \"" = "\"", + "Single Quote '" = "'", + "None" = ""), + "Double Quote \""))), + fluidRow(column(4, p('Decimal mark')), + column(8, selectInput('cpCSVdec', NULL, + c('Period "."' = ".", + 'Comma ","' = ","), + 'Period "."'))), + fluidRow(column(4, p('File Encoding')), + column(8, selectInput('cpCSVencoding', NULL, + c("unknown", "UTF-8", "Latin-1"), + "unknown")))), + + ## Options for ods files + conditionalPanel( + "input.cpNDfiletype == 'ods'", + fluidRow(column(4, p('Sheet')), + column(8, numericInput('cpODSsheet', NULL, 1, 1, + step = 1))), + checkboxInput("cpODScolnames", "Column names", TRUE), + fluidRow(column(4, p('NA symbol')), + column(8, textInput('cpODSna', NULL, value = ""))), + fluidRow(column(4, p('Skip rows')), + column(8, numericInput('cpODSskip', NULL, 0, 0, + step = 1))), + fluidRow(column(4, p('Specify range')), + column(8, textInput('cpODSrange', NULL, value = ""))) + ), + + ## Options for xls / xlsx files + conditionalPanel( + "input.cpNDfiletype == 'excel'", + fluidRow(column(4, p('Column names')), + column(8, selectInput('cpXLScolnames', NULL, + choices = c("Yes" = "TRUE", + "No" = "FALSE"), + selected = TRUE ))), + fluidRow(column(4, p('Skip rows')), + column(8, numericInput('cpXLSskip', NULL, 0, 0, + step = 1))), + fluidRow(column(4, p('Trim whitespaces')), + column(8, selectInput('cpXLStrim', NULL, + choices = c("Yes" = "TRUE", + "No" = "FALSE"), + selected = TRUE ))), + checkboxInput("cpXLSsheet", + "Specify Worksheet", FALSE), + conditionalPanel("input.cpXLSsheet", + textInput("cpXLSsheet_specs", NULL, "")), + checkboxInput("cpXLSrange", "Specify Range", FALSE), + conditionalPanel("input.cpXLSrange", + textInput("cpXLSrange_specs", NULL, "") + )), + + ## Options for SPSS files + conditionalPanel( + "input.cpNDfiletype == 'spss'", + fluidRow( + column(4, p('Skip rows')), + column(8, numericInput('cpSPSSskip', NULL, 0, 0, step = 1))), + checkboxInput("cpSPSSna", "User-defined NA", FALSE)), + + ## Options for SAS files + conditionalPanel( + "input.cpNDfiletype == 'sas_data'", + fluidRow( + column(4, p('Skip rows')), + column(8, numericInput("cpSASskip", NULL, 0, 0, step = 1))), + checkboxInput("cpSAScatalog", "Use catalog file", FALSE), + conditionalPanel( + "input.cpSAScatalog", + fileInput("cpSAScatalog_file", "Catalog file")) + ), + + ## Options for STATA dta files + conditionalPanel( + "input.cpNDfiletype == 'stata'", + checkboxInput("cpSTATAfactors", "Convert factors", FALSE)) + ) + ) + }), + + uiOutput("cpDataMessage"), + hr(), + h4("Data"), + + fluidRow( + column(6, numericInput( + "cpNrows", "N. rows for working sample", 500, 1, step = 1)), + column(6, numericInput( + "cpNskip", "Rows to skip before working sample", 0, 0, step = 1)) + ), + + fluidRow( + column(3, p(strong("*ID"))), + column(5, uiOutput("cpIdcolUI")), + column(4, p("Name of the id variable, unique identifier of each text")) + ), + fluidRow( + column(3, p(strong("*Text"))), + column(5, uiOutput("cpTextcolUI")), + column(4, p("Name of the text variables: if more than one, texts are concatenated in the specified order")) + ), + fluidRow( + column(3, p(strong("*Tags"))), + column(5, uiOutput("cpTagcolUI")), + column(4, p("(optional) Names of variables that are already tagged: each will create a new scheme")) + ), + fluidRow( + column(3, p(strong("Comments"))), + column(5, uiOutput("cpCommcolUI")), + column(4, p("(optional) Name of the comments variable")) + ), + fluidRow( + column(3, p(strong("Context"))), + column(5, uiOutput("cpContextcolUI")), + column(4, p("(optional) Names of variables not used in the models, but may be displayed during tagging")) + ), + + + hr(), + h4("System"), + + fluidRow( + column(3, checkboxInput("cp_use_python", "Python backend", FALSE)), + column(5, conditionalPanel( + "input.cp_use_python", + textInput("cp_which_python", NULL, value = "python3", + placeholder = "(custom python path)"))), + column(4, conditionalPanel( + "input.cp_use_python", + p("This must be a working python3 environment, with the required modules installed (see documentation)"))) + ), + conditionalPanel("input.cp_use_python", list( + fluidRow( + column(3, p("Language")), + column(5, selectInput("cp_lang", NULL, modelnames_labels, "en")), + column(4, p("Used to preset tokenization and embedding models")) + ), + fluidRow( + column(3, checkboxInput("cp_use_spacy", "SpaCy tokenization", FALSE)), + column(5, conditionalPanel("input.cp_use_spacy", uiOutput("cp_spacyUI"))), + column(4, p("Name of the spacy tokenizer model, used in DTM and word embeddings")) + ), + conditionalPanel("input.cp_use_spacy", fluidRow( + column(3), + column(9, uiOutput("cp_spacyDlUI"))) + ), + fluidRow( + column(3, checkboxInput("cp_use_ft", "FastText word embeddings", FALSE)), + column(5, conditionalPanel("input.cp_use_ft", uiOutput("cp_ftUI"))), + column(4, p("Path to the local fasttext model binary")) + ), + conditionalPanel("input.cp_use_ft", fluidRow( + column(3), + column(9, uiOutput("cp_ftDlUI")))), + fluidRow( + column(3, checkboxInput("cp_use_sb", "SBERT sentence embeddings", FALSE)), + column(5, conditionalPanel("input.cp_use_sb", uiOutput("cp_sbUI"))), + column(4, p("(GPU recommended) Name or path of the sentence-transformers model")) + ), + + conditionalPanel("input.cp_use_python", { + checkboxInput("cp_use_gpu", "GPU support (CUDA, for SBERT and BERT)", FALSE) + }) + )), + + uiOutput("cpConfirmMessage"), + footer = tagList(actionButton("cpConfirm", "Create"), + modalButton("Cancel")))) + }) + + observeEvent(input$cpDatafileGo, { + dataname <- input$cpDatafile + dataname <- gsub("[.]feather$", "", dataname) + tmpdat <- try(arrow::read_feather( + paste0(gsub("/+$", "/", paste0(input$cpDatadir, "/")), + dataname, ".feather"))) + if (inherits(tmpdat, "try-error")) { + values$cpDataMessage <- paste0( + "Data import error: ", as.character(tmpdat)) + values$cpNewData <- NULL + return(NULL) + } + if (!is.data.frame(tmpdat)) { + values$cpDataMessage <- "Data import error: object not a data.frame" + values$cpNewData <- NULL + return(NULL) + } + if (ncol(tmpdat) < 2) { + values$cpDataMessage <- "Data import error: only one column detected" + values$cpNewData <- NULL + return(NULL) + } + + values$cpDataMessage <- paste0( + "Data import success: ", nrow(tmpdat), " rows, ", ncol(tmpdat), " cols.") + values$cpNewData <- tmpdat + values$cpNewDataname <- dataname + + }) + + ## Data import mechanism + observeEvent(input$cpNDfile, { + req(input$cpNDfile) + if(input$cpNDfiletype == "csv_txt") { + res <- try(read.csv( + input$cpNDfile$datapath, + header = as.logical(input$cpCSVheader), sep = input$cpCSVsep, + quote = input$cpCSVquote, dec = input$cpCSVdec, stringsAsFactors = FALSE, + encoding = input$cpCSVencoding, check.names = TRUE)) + + } else if(input$cpNDfiletype == "feather") { + res <- try(arrow::read_feather(input$cpNDfile$datapath)) + + } else if(input$cpNDfiletype == "parquet") { + res <- try(arrow::read_parquet(input$cpNDfile$datapath)) + + } else if (input$cpNDfiletype == "ods") { + range <- input$cpODSrange + if (range == "") range <- NULL + res <- try(readODS::read_ods( + path = input$cpNDfile$datapath, sheet = input$cpODSsheet, + col_names = input$cpODScolnames, na = input$cpODSna, + skip = input$cpODSskip, range = input$cpODSrange)) + + } else if(input$cpNDfiletype == "excel"){ + column_names <- as.logical(input$cpXLScolnames) + trim_spaces <- as.logical(input$cpXLStrim) + the.range <- NULL + if(input$cpXLSrange == TRUE & input$cpXLSrange_specs != "") + the.range <- input$cpXLSrange_specs + the.sheet <- NULL + if(input$cpXLSsheet == TRUE & input$cpXLSsheet_specs != "") + the.sheet <- input$cpXLSsheet_specs + res <- try(data.frame(readxl::read_excel( + dataFile$datapath, col_names = column_names, range = the.range, + sheet= the.sheet, trim_ws = trim_spaces, skip = rows_to_skip))) + + } else if(input$cpNDfiletype == "spss"){ + res <- try(data.frame(haven::read_spss( + file = input$cpNDfile$datapath, + skip= input$cpSPSSskip, user_na = input$cpSPSSna))) + if (!inherits(res, "try-error")) { + res <- data.frame(lapply(res, function(x) { + attr(x, "format.spss") <- NULL + if ("haven_labelled" %in% class(x)) + x <- haven::as_factor(x, levels= "labels") + x + })) + } + + } else if(input$cpNDfiletype == "sas_data") { + res <- try(data.frame(haven::read_sas( + data_file = input$cpNDfile$datapath, + catalog_file = input$cpSAScatalog_file$datapath, + skip = input$cpSASskip))) + + } else if(input$cpNDfiletype == "stata") { + res <- try(data.frame(foreign::read.dta( + file = input$cpNDfile$datapath, convert.factors = input$cpSTATAfactors))) + } + + if(inherits(res, "try-error")) { + values$cpDataMessage <- paste0("Data import error: ", res) + values$cpNewData <- NULL + return(NULL) + } + if (ncol(res) < 2) { + values$cpDataMessage <- "Data import error: only one column detected" + values$cpNewData <- NULL + return(NULL) + } + values$cpDataMessage <- paste0( + "Data import success: ", nrow(res), " rows, ", ncol(res), " columns.") + values$cpNewData <- res + }) + + ## New project confirm + observeEvent(input$cpConfirm, { + if (verbose) + cat("DEBUG cpConfirm\n") + newconf <- list() + + tmpdir <- gsub("/+$", "/", paste0(input$cpDatadir, "/")) + if (!dir.exists(tmpdir)) + dir.create(tmpdir) + newconf$datadir <- tmpdir + + tmpname <- cleanFileName(input$cpName) + if (!nchar(tmpname)) { + values$cpConfirmMessage <- "Project name is required" + return(NULL) + } + if (dir.exists(paste0(newconf$datadir, tmpname))) { + values$cpConfirmMessage <- paste0( + "A project with the same name (", tmpname, + ") already exists in the data directory.") + return(NULL) + } + newconf$projectid <- tmpname + + if (!is.data.frame(values$cpNewData)) { + values$cpConfirmMessage <- "Import data first" + return(NULL) + } + + if (input$cpDoImport) { + newconf$dataname <- newconf$projectid + } else + newconf$dataname <- values$cpNewDataname + + if (any(is.na(values$cpNewData[[input$cpIdcol]]))) { + values$cpConfirmMessage <- "ID variable contains missing values" + return(NULL) + } + if (any(duplicated(values$cpNewData[[input$cpIdcol]]))) { + values$cpConfirmMessage <- "ID variable contains duplicates" + return(NULL) + } + newconf$idcol <- input$cpIdcol + + if (!length(input$cpTextcol)) { + values$cpConfirmMessage <- "Select at least one text variable" + return(NULL) + } + newconf$textcols <- input$cpTextcol + + if (input$cpCommcol == "none") { + newconf$commcol <- paste0("comm_", newconf$projectid) + } else + newconf$commcol <- input$cpCommcol + + if (!length(input$cpTagcol)) { + newconf$tagcols <- "scheme0" + } else + newconf$tagcols <- input$cpTagcol + + newconf$contextcols <- input$cpContextcol + + newconf$dataNrows <- input$cpNrows + newconf$dataSkipRows <- input$cpNskip + + if (input$cp_use_python) { + pytest <- try(system(paste(input$cp_which_python, "--version"), + intern = TRUE)) + if (inherits(pytest, "try-error")) { + values$cpConfirmMessage <- paste( + "Python path `", input$cp_which_python, "` not valid") + return(NULL) + } + } + newconf$use_python <- input$cp_use_python + newconf$python <- input$cp_which_python + newconf$use_gpu <- input$cp_use_gpu + + if (input$cp_use_spacy) { + sptest <- system(paste0( + newconf$python, " -m spacy info ", input$cp_use_spacy_model), intern = TRUE) + if (length(sptest) == 0) { + values$cpConfirmMessage <- paste( + "Error loading spacy, check that it is installed in the specified python env") + return(NULL) + } + if (length(attr(sptest, "status"))) { + values$cpConfirmMessage <- paste( + "Error loading spacy model, check that it has been downloaded") + return(NULL) + } + } + newconf$use_spacy <- input$cp_use_spacy + newconf$use_spacy_model <- input$cp_use_spacy_model + + if (input$cp_use_ft) if (!file.exists(input$cp_use_ft_model)) { + values$cpConfirmMessage <- paste( + "Error loading fasttext model, check the specified path") + return(NULL) + } + newconf$use_ft <- input$cp_use_ft + newconf$use_ft_model <- input$cp_use_ft_model + + newconf$use_sb <- input$cp_use_sb + newconf$use_sb_model <- input$cp_use_sb_model + + writeLines(RJSONIO::toJSON(newconf), + paste0("tigger_", newconf$projectid, ".json")) + + values$confnames <- c(newconf$projectid, values$confnames) + values$conf <- newconf + + ## On new data import, write a feather file with the name of the project + if (input$cpDoImport) + arrow::write_feather(values$cpNewData, paste0( + newconf$datadir, newconf$dataname, ".feather" + )) + + removeModal() + }) + + ############################################################################## + ## Scheme management + ############################################################################## + + ## Print active scheme in scheme management panel + output$printScheme <- renderUI(h4(input$selectScheme)) + + ## Create new scheme on request, with modal dialog + observeEvent(input$createScheme, { + if (verbose) + cat("DEBUG create scheme\n") + showModal(modalDialog( + title = "Create tagging scheme", + p(paste0("This will create a new scheme for this project")), + + textInput("newScheme", label = NULL, placeholder = "Required: new scheme name"), + textAreaInput("newSchemeDescr", label = NULL, + placeholder = "Optional: scheme description"), + checkboxInput("schemeDuplicate", "Duplicate existing scheme:"), + conditionalPanel( + "input.schemeDuplicate", + selectInput("schemeDuplicateFrom", NULL, rev(values$conf$tagcols))), + + footer = tagList(actionButton("createSchemeConfirm", "Create"), + modalButton("Cancel")))) + }) + + observeEvent(input$createSchemeConfirm, { + if (is.null(ok.data())) return(NULL) + if (verbose) + cat("DEBUG create scheme event\n") + + ## Make sure new scheme name is a correct file name + if (!grepl("\\S", input$newScheme)) { + showNotification(type = "error", + "Cannot create scheme: name empty.") + return(NULL) + } + + ## Check that new scheme name doesn't exist + ## Re-read tagcols from conf file, to prevent scheme disappearances + tmpname <- cleanFileName(input$newScheme) + tagpath <- paste0(values$projectdir, tmpname, ".csv") + confpath <- paste0("tigger_", values$conf$projectid, ".json") + if (verbose) + cat("DEBUG create scheme check json:", confpath,"\n") + tmp_tagcols <- RJSONIO::fromJSON(confpath)$tagcols + if (verbose) + cat("DEBUG create scheme json OK\n") + if (tmpname %in% tmp_tagcols | file.exists(tagpath)) { + showNotification(type = "error", + "Cannot create scheme: name already exists.") + return(NULL) + } + + ## Create new scheme description + if (nchar(input$newSchemeDescr)) + writeLines(input$newSchemeDescr, + paste0(values$projectdir, tmpname, ".txt")) + + ## Update conf file and reactives + values$conf$tagcols <- c(tmp_tagcols, tmpname) + # values$confs[[values$conf$projectid]]$tagcols <- c(tmp_tagcols, tmpname) + writeLines(RJSONIO::toJSON(values$conf), confpath) + + if (input$schemeDuplicate) { + tmpmat <- matrix(values$tagmat[, input$schemeDuplicateFrom]) + tmpmathist <- matrix(values$tagmathist[, input$schemeDuplicateFrom]) + } else { + tmpmat <- matrix(rep(NA, nrow(ok.data()))) + tmpmathist <- matrix(rep(NA, nrow(ok.data()))) + } + colnames(tmpmat) <- tmpname + colnames(tmpmathist) <- tmpname + + values$tagmat <- cbind(values$tagmat, tmpmat) + values$tagmathist <- cbind(values$tagmat, tmpmathist) + + ## Create new tagging file if duplicating + if (input$schemeDuplicate) { + export <- data.frame(ok.data()[, values$conf$idcol], + values$tagmat[, tmpname]) + colnames(export) <- c(values$conf$idcol, tmpname) + export <- na.omit(export) + write.table(export, tagpath, sep = ",", qmethod = "double", + fileEncoding = "UTF-8", row.names = FALSE, + append = FALSE, col.names = TRUE) + } + + cat("DEBUG create scheme exit\n") + removeModal() + }) + + ## Button action: delete scheme + observeEvent(input$schemeDelete, { + if (verbose) + cat("DEBUG delete scheme\n") + if (length(values$conf$tagcols) < 2) { + showNotification(type = "error", + "Cannot delete: at least one scheme must remain.") + return(NULL) + } + showModal(modalDialog( + title = "Delete tagging scheme", + paste0("Confirm delete '", input$selectScheme, + "': ", sum(!is.na(values$label)), " tags will be deleted", + ", along with scheme description."), + footer = tagList(actionButton("schemeDeleteConfirm", "Delete"), + modalButton("Cancel")))) + }) + + observeEvent(input$schemeDeleteConfirm, { + file.remove(paste0(values$projectdir, input$selectScheme, ".csv")) + file.remove(paste0(values$projectdir, input$selectScheme, ".txt")) + file.remove(paste0(values$projectdir, input$selectScheme, "_regex.json")) + values$conf$tagcols <- + values$conf$tagcols[values$conf$tagcols != input$selectScheme] + writeLines(RJSONIO::toJSON(values$conf), + paste0("tigger_", input$selectProject, ".json")) + removeModal() + }) + + + observeEvent(input$schemeDescrSave, { + req(input$selectScheme) + writeLines(input$schemeDescr, + paste0(values$conf$datadir, values$conf$projectid, + "/", input$selectScheme, ".txt")) + }) + + + ############################################################################## + ############################################################################## + + ## Tag selection : populate + observe({ + values$conf$tagcols + updateSelectInput( + # session, "selectScheme", choices = c(rev(values$conf$tagcols), "")) + session, "selectScheme", choices = rev(values$conf$tagcols)) + }) + + + ## Populate oracle buttons + output$oracleButtons <- renderUI({ + if (is.null(ok.data())) return(NULL) + # if (is.null(values$label)) return(NULL) + if (is.null(values$uniqueLabels)) return(NULL) + if (verbose) + cat("DEBUG populate oracle buttons\n") + + do.call(fluidRow, lapply(values$uniqueLabels, function(ilab) + actionButton(paste0("oracle_", label2hash(ilab)), + strong(ilab)))) + }) + + ## Populate maxprob variable selector + observe({ + updateSelectInput(session, "maxprobWhich", choices = values$uniqueLabels) + updateSelectInput(session, "taggedWhich", + choices = c("All" = "all", values$uniqueLabels)) + }) + + ## Data panel message + observeEvent(ok.data(), { + if (verbose) + cat("DEBUG data panel message\n") + if (is.null(ok.data())) { + output$dataMessage <- renderUI(p("Importing data...")) + } + output$dataMessage <- NULL + }) + + ## On config change, update dataView and retag options + observeEvent(list(values$conf), { + + if (is.null(values$conf)) return(NULL) + output$panelData <- renderUI(list( + DT::dataTableOutput("dataView") + )) + + output$panelRetag <- renderUI(c( + list( + br(), + h4("Rename tags"), + fluidRow( + column(4, strong("Current")), + column(2, strong("Count")), + column(6, strong("New")) + ), + br()), + lapply(values$uniqueLabels, function(iLab) { + iHash <- label2hash(iLab) + fluidRow( + column(4, p(iLab)), + column(2, p(sum(values$label == iLab, na.rm = TRUE))), + column(4, textInput( + paste0("newtag_", iHash), NULL, + placeholder = "(new name, leave empty to untag)")), + column(2, actionButton(paste0("retag_", iHash), "Rename"))) + }) + )) + }) + + output$panelExtraRegex <- renderUI(list( + p(strong("Regex predictors")), + fluidRow( + column(8, textInput("extraRegexText", NULL, + placeholder = "(new regex)", width = "100%")), + column(4, actionButton( + "extraRegexSearch", strong("+"), width = "100%")) + ), + fluidRow( + style="overflow-y:scroll; max-height: 25vh", + fluidRow( + column(2, strong("")), + column(1, strong("Use")), + column(1, strong("Case")), + column(1, p(strong("N"))), + column(7, p(strong("Regex"))) + ), + + lapply(values$extraRegex, function(iRegex) { + iHash <- label2hash(iRegex) + tmp_cased <- values[[paste0("caseregex_", iHash)]] + if (is.null(tmp_cased)) tmp_cased <- FALSE + fluidRow( + column(2, actionButton(paste0("delregex_", iHash), "🗑️", width="100%")), + column(1, checkboxInput( + paste0("useregex_", iHash), NULL, + value = isolate(!isFALSE(values[[paste0("useregex_", iHash)]])))), + column(1, checkboxInput( + paste0("caseregex_", iHash), NULL, + value = isolate(isTRUE(values[[paste0("caseregex_", iHash)]])))), + column(1, br(), p(sum( + stringi::stri_detect_regex( + ok.nicetext(), iRegex, + opts_regex = list(case_insensitive = !tmp_cased)), + na.rm = TRUE))), + column(7, br(), p(iRegex)) + ) + }) + ) + + )) + + ## Process new regex + observeEvent(input$extraRegexSearch, { + if (is.null(ok.nicetext())) return(NULL) + req(nchar(input$extraRegexText) > 0) + if (input$extraRegexText %in% values$extraRegex) return(NULL) + try_regex <- try(stringi::stri_detect_regex( + ok.nicetext(), input$extraRegexText, + opts_regex = list(case_insensitive = TRUE))) + if (inherits(try_regex, "try-error")) return(NULL) + new_regex <- input$extraRegexText + new_hash <- label2hash(new_regex) + values$extraRegex <- c(new_regex, values$extraRegex) + names(values$extraRegex)[1] <- new_hash + if (verbose) + cat("DEBUG regex search", input$extraRegexText, "\n") + }) + + ## Regex predictors: populate delete and use events on first creation + useregex_trigger <- reactiveVal(0) + delregex_trigger <- reactiveVal(0) + caseregex_trigger <- reactiveVal(0) + observeEvent(list(values$extraRegex), { + if (!length(values$extraRegex)) return(NULL) + if (any(! values$extraRegex %in% values$extraRegexHist)) { + if (verbose) + cat("DEBUG regex predictor names updates\n") + tmpregex <- values$extraRegex[! values$extraRegex %in% values$extraRegexHist] + values$extraRegexHist <- c(tmpregex, values$extraRegexHist) + + c(lapply(1:length(tmpregex), function(iregex) { + observeEvent( + input[[paste0("delregex_", names(tmpregex)[iregex])]], { + if (verbose) + cat("DEBUG regex delete", names(tmpregex)[iregex], "\n") + values$extraRegex <- values$extraRegex[values$extraRegex != tmpregex[iregex]] + delregex_trigger(delregex_trigger() + 1) + }) + }), + lapply(1:length(tmpregex), function(iregex) { + tmpname <- paste0("useregex_", names(tmpregex)[iregex]) + observeEvent( + input[[tmpname]], { + values[[tmpname]] <- input[[tmpname]] + useregex_trigger(useregex_trigger() + 1) + }) + }), + lapply(1:length(tmpregex), function(iregex) { + tmpname <- paste0("caseregex_", names(tmpregex)[iregex]) + observeEvent( + input[[tmpname]], { + values[[tmpname]] <- input[[tmpname]] + caseregex_trigger(caseregex_trigger() + 1) + }) + }) + ) + } + }) + + observeEvent(list(delregex_trigger(), useregex_trigger(), caseregex_trigger()), { + req(ok.data(), input$selectScheme) + regex_all <- values$extraRegex + values$regex_inuse <- sapply(names(regex_all), function(iname) + values[[paste0("useregex_", iname)]]) + values$regex_case <- sapply(names(regex_all), function(iname) + values[[paste0("caseregex_", iname)]]) + if (verbose) if (any(values$regex_inuse)) + cat("DEBUG regex in use", paste0(collapse = " ", regex_all[values$regex_inuse]), "\n") + writeLines( + RJSONIO::toJSON(lapply(FUN = unname, list( + regex = regex_all, use = values$regex_inuse, case = values$regex_case))), + paste0(values$projectdir, input$selectScheme, "_regex.json")) + }) + + output$dataView <- DT::renderDataTable({ + # if (is.null(ok.text()) | ok.data.running()) return(NULL) + req(ok.text()) + if (verbose) + cat("DEBUG enter dataView\n") + if (verbose) + cat("DEBUG render dataView\n") + DT::datatable( + rownames = F, + data.frame( + row = 1:nrow(ok.data()), + id = ok.data()[, values$conf$idcol], + text = ok.text()), + options = list( + pageLength = 3, + columnDefs = list(list( + targets = 2, + render = DT::JS( + "function(data, type, row, meta) {", + "return type === 'display' && data.length > 200 ?", + "'' + data.substr(0, 200) + '...' : data;", + "}") + )) + ), + callback = DT::JS('table.page(0).draw(false);') + ) + }) + + ## Triggers for mass rename, created with each new label, with confirm + observeEvent(values$uniqueLabelsAll, { + if (is.null(values$uniqueLabelsAll)) return(NULL) + notthere <- unique(na.omit( + values$uniqueLabelsAll[! values$uniqueLabelsAll %in% values$retagLabels])) + lapply( + notthere, + function(iTag) { + iHash <- label2hash(iTag) + + observeEvent(input[[paste0("retag_", iHash)]], { + tmp_oldtag <- iTag + tmp_retagged <- which(values$label == tmp_oldtag) + tmp_newtag <- gsub( + "^\\s+|\\s+$", "", input[[paste0("newtag_", iHash)]]) + showModal(modalDialog( + title = "Mass retag", + paste0("Confirm retag: ", length(tmp_retagged), " ", + tmp_oldtag, " tags will be ", + ifelse(nchar(tmp_newtag), + paste("renamed to", tmp_newtag) , "deleted")), + footer = tagList(actionButton(paste0("retag_confirm_", iTag), "Retag"), + modalButton("Cancel")))) + }) + + observeEvent(input[[paste0("retag_confirm_", iTag)]], { + tmp_oldtag <- iTag + tmp_retagged <- which(values$label == tmp_oldtag) + tmp_newtag <- gsub( + "^\\s+|\\s+$", "", input[[paste0("newtag_", iHash)]]) + if (!nchar(tmp_newtag)) tmp_newtag <- NA + values$tagTrigger <- list( + id = ok.data()[[values$conf$idcol]][tmp_retagged], + tag = rep(tmp_newtag, length(tmp_retagged))) + values$queries <- c(values$queries, tmp_retagged) + + removeModal() + }) + + }) + values$retagLabels <- c(values$retagLabels, notthere) + }) + + + + ############################################################################## + ## UI rendering of Panel Tagging / Visualization + ############################################################################## + + #################### + ## Tagging left panel + + + # ## Trigger from Oops + # observeEvent(input$oops, { + # if (is.null(ok.data())) + # return(NULL) + # if (verbose) + # cat("DEBUG enter Oops\n") + # if (!is.null(values$retagged)) if (values$queries[length(values$queries)] == values$retagged[length(values$retagged)]) + # values$retagged <- values$retagged[-length(values$retagged)] + # values$label[values$queries[length(values$queries)]] <- NA + # if (ok.commentCol() != "(None)") + # values$comment[values$queries[length(values$queries)]] <- ok.data()[values$queries[length(values$queries)], ok.commentCol()] + # values$queries <- values$queries[-length(values$queries)] + # ## tagTrigger ? + # + # ready2tag(0) + # queryNext(queryNext() + 1) + # }) + + + ## Tagging triggers for Oracle buttons, created with each new label + observeEvent(values$uniqueLabelsAll, { + if (is.null(values$label)) return(NULL) + if (is.null(values$uniqueLabelsAll)) return(NULL) + if (all(is.na(values$label))) return(NULL) + notthere <- unique(na.omit( + values$uniqueLabelsAll[! values$uniqueLabelsAll %in% values$oracleLabels] + )) + if (!length(notthere)) return(NULL) + if (verbose) + cat("DEBUG triggers for oracle buttons\n") + lapply( + notthere, + function(iLab) { + observeEvent( + input[[paste0("oracle_", label2hash(iLab))]], { + if (is.null(ok.text())) return(NULL) + if (is.null(values$newQuery)) return(NULL) + if (!ready2tag()) {cat("DEBUG not ready to tag in Button\n"); return(NULL)} + # ready2tag(0) + # if (input$allowRetag | sum(is.na(values$label)) == 0) + if (!is.na(values$label[values$lastQuery])) + values$retagged <- c(values$retagged, values$lastQuery) + # values$label[values$lastQuery] <- iLab + values$comment[values$lastQuery] <- input$currentComment + # values$queries <- c(values$queries, values$lastQuery) + values$tagTrigger <- list( + id = ok.data()[values$lastQuery, values$conf$idcol], + tag = iLab, + comment = input$currentComment) + }) + }) + values$oracleLabels <- c(values$oracleLabels, notthere) + }) + + ## Trigger from Oracle Create value button + observeEvent(input$currentAction, { + if (is.null(ok.data())) return(NULL) + if (is.null(values$newQuery)) return(NULL) + if (verbose) + cat("DEBUG enter create value\n") + # if (input$allowRetag | sum(is.na(values$label)) == 0) + if (!is.na(values$label[values$lastQuery])) + values$retagged <- c(values$retagged, values$lastQuery) + if (input$newLab == "") { + return(NULL) + } else { + # values$label[values$lastQuery] <- input$newLab + updateTextInput(session, "newLab", value= "") + } + values$comment[values$lastQuery] <- input$currentComment + # values$queries <- c(values$queries, values$lastQuery) + values$tagTrigger <- list( + id = ok.data()[values$lastQuery, values$conf$idcol], + tag = input$newLab, + comment = input$currentComment) + }) + + ## Trigger from Oracle Confirm button + observeEvent(input$oracleConfirm, { + req(ok.data(), ok.train(), values$newQuery) + # if (is.null(ok.data())) return(NULL) + # if (is.null(values$newQuery)) return(NULL) + if (!ready2tag()) {cat("DEBUG not ready to tag in OC\n"); return(NULL)} + # ready2tag(0) + if (!ok.train.possible()) return(NULL) + if (verbose) + cat("DEBUG oracle confirm\n") + # if (input$allowRetag | sum(is.na(values$label)) == 0) + if (!is.na(values$label[values$lastQuery])) + values$retagged <- c(values$retagged, values$lastQuery) + # values$label[values$lastQuery] <- ok.predlab1()[values$lastQuery] + # values$queries <- c(values$queries, values$lastQuery) + values$comment[values$lastQuery] <- input$currentComment + values$tagTrigger <- list( + id = ok.data()[values$lastQuery, values$conf$idcol], + tag = ok.predlab1()[values$lastQuery], + comment = input$currentComment) + }) + + ## Trigger for CV glm (async) + glmCV_nclicks <- reactiveVal(0) + observeEvent(input$glmCV, { + if (is.null(ok.predictor())) return(NULL) + if (glmCV_nclicks() != 0) { + showNotification("LASSO CV already running") + return(NULL) + } + glmCV_nclicks(1) + glmCV_future <- reactiveVal() + + tmpvalues <- values$label + tmppred <- ok.predictor() + for (ival in na.omit(unique(tmpvalues))) + if (sum(tmpvalues == ival, na.rm = T) <= 8) + tmpvalues[which(tmpvalues == ival)] <- NA + glmCV_async <- future(seed = TRUE, { + glmnet::cv.glmnet( + x= tmppred[!is.na(tmpvalues), ], + y= as.factor(tmpvalues[!is.na(tmpvalues)]), + family= "multinomial") + }) %...>% glmCV_future() + + glmCV_async <- catch(glmCV_async, function(e) { + showNotification(type = "error", paste0("LASSO CV error: ", e)) + }) + glmCV_async <- finally(glmCV_async, function() { + glmCV_nclicks(0) + if (is.null(glmCV_future())) return(NULL) + updateNumericInput(session, "glmLambda", + value = glmCV_future()$lambda.min) + }) + NULL + }) + + ## Trigger for CV liblinear (async) + liblinCV_nclicks <- reactiveVal(0) + observeEvent(input$liblinCV, { + if (is.null(ok.predictor())) return(NULL) + if (liblinCV_nclicks() != 0) { + showNotification("LibLineaR CV already running") + return(NULL) + } + liblinCV_nclicks(1) + liblinCV_future <- reactiveVal() + + tmpvalues <- values$label + tmppred <- ok.predictor() + for (ival in na.omit(unique(tmpvalues))) + if (sum(tmpvalues == ival, na.rm = T) <= 3) + tmpvalues[which(tmpvalues == ival)] <- NA + + liblinCV_async <- future(seed = TRUE, { + LiblineaR::LiblineaR( + data= tmppred[!is.na(tmpvalues), ], + target= as.factor(tmpvalues[!is.na(tmpvalues)]), + type= 0, findC= T) + }) %...>% liblinCV_future() + liblinCV_async <- catch(liblinCV_async, function(e) { + showNotification(type = "error", paste0("LibLineaR CV error: ", e)) + }) + liblinCV_async <- finally(liblinCV_async, function() { + liblinCV_nclicks(0) + if (is.null(liblinCV_future())) return(NULL) + updateNumericInput(session, "liblinCost", value= liblinCV_future()) + }) + NULL + }) + + + output$currentContext <- renderUI({ + if (verbose) + cat("DEBUG 1rentContext\n") + if ((! is.null(ok.text())) & (length(ok.contextCol()) > 0)) + HTML(paste(paste(ok.contextCol(), + as.character(ok.data()[values$newQuery, ok.contextCol()]), + sep = " : "), + collapse= "

    ")) + }) + + output$makeOracleConfirm <- renderUI({ + if (verbose) + cat("DEBUG update printPredicted\n") + if (is.null(ok.pred())) { + tmp <- "No prediction" + } else if (is.null(values$newQuery)) { + tmp <- "No query" + } else { + tmp <- paste0( + ifelse(is.na(values$label[values$newQuery]), "", "Pred: "), + ok.predlab1()[values$newQuery], + " (", round(max(ok.pred()[values$newQuery, ]), 3), ")") + } + res <- list(actionButton("oracleConfirm", label= paste("🤖", tmp), width = "100%")) + if (!is.null(values$label) & !is.null(values$newQuery)) { + if (!is.na(values$label[values$newQuery])) + res <- list(res[[1]], HTML(paste0( + "
    Current: ", values$label[values$newQuery], + "
    "))) + } + res + }) + + + ## Show training diagnostics on new model train + output$trainDiagno <- + renderText(paste0(ok.msgtagged(), "\n", values$modelDiagno)) + + ## Diagnostics event + observeEvent(diagTrigger(), { + if (verbose) + cat("DEBUG update modelDiagno\n") + if (is.null(ok.data())) { + values$modelDiagno <- "No model" + return(NULL) + } + if (!ok.train.possible()) { + values$modelDiagno <- "No model (not enough labelled data)." + return(NULL) + } + + if (input$predModel == "random forest") { + tmp <- paste0( + "Train Accuracy: ", + round(100 * mean(ok.predlab1()[!is.na(values$label)] == + values$label[!is.na(values$label)]), 1), + "% ; Wtd. F1: ", + sprintf("%f", round(wtdF1(values$label[!is.na(values$label)], + ok.predlab1()[!is.na(values$label)]), 3)), + "%\nOOB Accuracy : ", + round(100 * mean( + colnames(ok.train()$predictions)[apply(ok.train()$predictions, 1, which.max)] == + values$label[!is.na(values$label)]), 1), + "% ; Wtd. F1: ", + sprintf("%.3f", wtdF1( + values$label[!is.na(values$label)], + colnames(ok.train()$predictions)[apply(ok.train()$predictions, 1, which.max)])) + ) + } else if (input$predModel %in% c("lasso", "linear", "naive bayes", "knn")) { + tmp <- paste0( + "Train Accuracy: ", + round(100 * mean(ok.predlab1()[!is.na(values$label)] == + values$label[!is.na(values$label)]), 1), + "% ; Wtd. F1: ", + sprintf("%.3f", wtdF1( + values$label[!is.na(values$label)], + ok.predlab1()[!is.na(values$label)]))) + } + values$modelDiagno <- tmp + }) + + ## Update comment input field + observeEvent(values$newQuery, { + if (is.null(ok.text())) return(NULL) + if (verbose) + cat("DEBUG update comment field\n") + updateTextInput(session, "currentComment", + value = values$comment[values$newQuery]) + }) + + + ## Save event: tag + comment in dedicated csv + observeEvent(values$tagTrigger, { + if (is.null(values$tagTrigger)) return(NULL) + if (verbose) + cat("DEBUG enter event tagTrigger", length(values$tagTrigger$id), "\n") + ready2tag(0) # Prevent new tagging until next query + dapath <- paste0(values$projectdir, ok.labelcol(), ".csv") + darows <- match(values$tagTrigger$id, ok.data()[, values$conf$idcol]) + + export <- data.frame(values$tagTrigger$id, values$tagTrigger$tag) + colnames(export) <- c(values$conf$idcol, ok.labelcol()) + if (verbose) + cat("DEBUG tagTrigger tag", + paste(export[, 1], export[, 2], collapse = " ; "), "\n") + write.table(export, dapath, sep = ",", qmethod = "double", + fileEncoding = "UTF-8", row.names = F, + append = ifelse(file.exists(dapath), T, F), + col.names = ifelse(file.exists(dapath), F, T)) + + if (any(nchar(values$tagTrigger$comment))) { + tmp_filter <- which(nchar(values$tagTrigger$comment) > 0) + dapath <- paste0(values$projectdir, values$conf$commcol, ".csv") + export <- data.frame(values$tagTrigger$id[tmp_filter], + values$tagTrigger$comment[tmp_filter]) + colnames(export) <- c(values$conf$idcol, values$conf$commcol) + if (verbose) + cat("DEBUG tagTrigger comment", export[1,1], export[1, 2]) + write.table(export, dapath, sep = ",", qmethod = "double", + fileEncoding = "UTF-8", row.names = F, + append = ifelse(file.exists(dapath), T, F), + col.names = ifelse(file.exists(dapath), F, T)) + } + + ## Update label hist + values$labelhist[match(values$tagTrigger$id, ok.data()[, values$conf$idcol])] <- + max(values$labelhist, na.rm = T) + 1 + ## Update wasTagged and wasRetagged + itag <- match(values$tagTrigger$id, ok.data()[, values$conf$idcol]) + if (input$sampleChoice == "untagged") { + values$wasTagged[itag] <- TRUE + } else if (input$sampleChoice == "tagged") { + values$wasRetagged[itag] <- TRUE + } else if (input$sampleChoice == "all") { + if (is.na(values$label[itag])) { + values$wasTagged[itag] <- TRUE + } else + values$wasRetagged[itag] <- TRUE + } + + ## Call queryNext event, or launch training + if (trainCountdown() == 1) { + trainCountdown(input$trainCountdown) + trainTrigger(trainTrigger() + 1) + } else if (trainCountdown() > 1) { + trainCountdown(trainCountdown() - 1) + queryNext(queryNext() + 1) + } else { + queryNext(queryNext() + 1) + } + + ## Update values$label etc + values$label[darows] <- values$tagTrigger$tag + values$queries <- c(values$queries, darows) + + if (verbose) + cat("DEBUG exit event tagTrigger", nrow(export), "rows\n") + }) + + + #################### + ## Tagging main panel + + output$textVisuCols <- renderUI({ + if (verbose) + cat("DEBUG render text/visu panel\n") + leftsize <- 6 + if (!input$panelText) leftsize <- 1 + if (input$panelText & !input$panelVisu) leftsize <- 11 + + fluidRow( + column( + leftsize, + conditionalPanel( + "input.panelText", + wellPanel(htmlOutput("currentText")) + ) + ), + column( + 12 - leftsize, + conditionalPanel( + "input.panelVisu", + fluidRow( + column(2, HTML(paste0('
    ', + actionButton("visuCompute", "🎲"), + '
    '))), + column(2, HTML(paste0('
    ', + actionButton("visuGoZoom", "🔍±"), + '
    '))), + column(2, HTML(paste0('
    ', + checkboxInput("visuLock", "🔒", value = FALSE), + '
    '))), + column(2, HTML(paste0('
    ', + checkboxInput("visuOptions", "🔧"), + '
    '))), + column(3, HTML(paste0('
    ', + numericInput("visuHeight", NULL, 400, 20, 2e12, 10), + '
    '))) + ), + conditionalPanel( + 'input.visuOptions', + flowLayout( + selectInput( + "visuAlgo", "Visu. method", c("umap", "t-sne"), "umap", width = "100%"), + selectizeInput( + "visuSource", "Visu. source", + c("Word counts (DFM)" = "dfm", + "Word embeddings (FastText)" = "ft", + "Sentence embeddings (SBERT)" = "sb", + "BERT predictions" = "bertpred", + "Regex" = "regex"), + c("sb", "ft"), multiple = TRUE) + ) + ), + conditionalPanel( # umap options + 'input.visuAlgo == "umap" & input.visuOptions', + flowLayout( + numericInput( + "umapNeighb", "Nb. Neighbors", 15, 1, 500, 1), + numericInput( + "umapSpread", "Spread", 1, 0, 16, 1e-2), + numericInput( + "umapMindist", "Min dist.", .001, 0, 16, 1e-3), + selectInput( + "umapMetric", "Metric", selected = "cosine", + c("euclidean", "cosine", "manhattan", "hamming", + "correlation", "categorical") + ) + ) + ), + conditionalPanel( # t-SNE options + 'input.visuAlgo == "t-sne" & input.visuOptions', + flowLayout( + numericInput( + "tsnePerplex", "Perplexity", 15, 1, 500, 1), + numericInput( + "tsneTheta", "Theta", .5, 0, 1, 1e-2), + numericInput( + "tsnePcaDim", "PCA dims", 50, 1, 100, 1) + ) + ), + uiOutput("visuMsg"), + plotOutput( + "visuPlot", width = "100%", + dblclick = "visuDblclick", + brush = brushOpts("visuBrush", resetOnNew = T)) + ) + ) + ) + }) + + ################## + ## Subpanel "Text" + ################## + + output$currentText <- renderUI({ + if (verbose) + cat("DEBUG render currentText\n") + if (is.null(ok.data()) | is.null(ok.nicetext())) + return(HTML("Import data to start training.")) + + if (input$strategy %in% c("entropy", "maxprob") & is.null(ok.train())) + return(HTML("No model: train one, or try 'random' or 'sequential' strategy")) + + if (is.null(values$newQuery)) + return(HTML("No query")) + + bastext <- ok.nicetext()[values$newQuery] + if (nchar(input$regexFilter) > 0) + bastext <- gsub(paste0("(", isolate(input$regexFilter), ")"), + "\\1", bastext, ignore.case = isolate(!input$regexCaseSens)) + baslen <- nchar(bastext) + if (!length(baslen)) { + if (nchar(input$regexFilter) > 0) + return(HTML("Regex filter: no match")) + return(HTML("No text matches the filters")) + } + if (is.na(baslen)) + return(HTML("No query")) + if (baslen > 1200) { + bastext <- paste0(substring(bastext, 1, 1200), '', + substring(bastext, 1201), "") + } + bastext <- gsub("\n", "
    ", bastext) + HTML(bastext) + }) + + + + ############################################################################## + ## Panel "Visualize" + ############################################################################## + + output$visuMsg <- renderUI({ + if (!is.null(values$visuMsg)) + # strong(values$visuMsg) + h5(values$visuMsg, style = "text-align:center") + }) + + ## Compute visualization points (async) + visu_nclicks <- reactiveVal(0) + ok.visu <- reactiveVal() + observe({ + if (!input$panelVisu) + values$visuGo <- FALSE + if (input$panelVisu & !values$visuGo) + values$visuGo <- TRUE + }) + observeEvent(list(values$visuGo, input$visuCompute), { + if (is.null(input$visuSource)) return(NULL) + if (!values$visuGo) return(NULL) + if (is.null(ok.data())) { + values$visuMsg <- "No data imported." + return(NULL) + } + + if (visu_nclicks() > 0) { + values$visuMsg <- "Already computing visualization" + return(NULL) + } + visu_nclicks(1) + + values$visuMsg <- "Computing visualization..." + + ## Prepare options and objects for async computation + dasources <- input$visuSource + daalgo <- input$visuAlgo + + if (verbose) + cat("DEBUG compute visu embedding: ", daalgo, paste(dasources, collapse = " "), "\n") + + visudat <- NULL + if ("dfm" %in% dasources) dadfm <- ok.dfm() + if ("ft" %in% dasources) { + visudat <- ok.data()[, values$cols_ft] + } + if ("sb" %in% dasources) { + if (is.null(visudat)) { + visudat <- ok.data()[, values$cols_sb] + } else + visudat <- cbind(visudat, ok.data()[, values$cols_sb]) + } + if ("bertpred" %in% dasources) { + if (is.null(visudat)) { + visudat <- ok.data()[, do.call(c, values$cols_bertpred)] + } else + visudat <- cbind(visudat, ok.data()[, do.call(c, values$cols_bertpred)]) + } + if ("regex" %in% dasources) { + if (is.null(visudat)) { + visudat <- ok.extraRegex.matrix() + } else + visudat <- cbind(visudat, ok.extraRegex.matrix()) + } + if (daalgo == "umap") { + visu_args <- list( + n_neighbors = input$umapNeighb, metric = input$umapMetric, + spread = input$umapSpread, min_dist = input$umapMindist) + } else if (daalgo == "t-sne") { + visu_args <- list( + perplexity = input$tsnePerplex, theta = input$tsneTheta, + initial_dims = input$tsnePcaDim, check_duplicates = FALSE) + } + + visu_async <- future(seed = TRUE, { + if ("dfm" %in% dasources) { + ## TODO: CA treatment buggy, switch to SVD? + # da_ca <- quanteda.textmodels::textmodel_ca( + # quanteda::as.dfm(dadfm), nd = min(100, nrow(dadfm))) + # da_ca <- (da_ca$rowcoord %*% diag(da_ca$sv))[, !is.na(da_ca$sv)] + da_ca <- as.matrix(dadfm) + if (is.null(visudat)) { + visudat <- da_ca + } else + visudat <- cbind(da_ca, visudat) + } + + if (daalgo == "umap") { + do.call(uwot::umap, c(list(visudat), visu_args)) + } else if (daalgo == "t-sne") { + visudat_dupli <- duplicated(visudat) + if (any(visudat_dupli)) + visudat[visudat_dupli, ] <- jitter(visudat[visudat_dupli, ]) + do.call(Rtsne::Rtsne, c(list(visudat), visu_args))$Y + } + }) %...>% ok.visu() + + visu_async <- catch(visu_async, function(error) { + values$visuMsg <- paste0("Error: ", error) + }) + visu_async <- finally(visu_async, function() { + visu_nclicks(0) + if (input$visuGoZoom == 0) { + values$visuMsg <- "Double-click to select, drag to select zooming zone." + } else + values$visuMsg <- NULL + + nice_choices <- c("FastText" = "ft", "SBERT" = "sb", "visu" = daalgo) + names(nice_choices)[3] <- ifelse(daalgo == "t-sne", "t-SNE", "UMAP") + updateSelectInput( + session, "dlEmbedSelect", choices = nice_choices, selected = daalgo) + + if (verbose) + cat("DEBUG Visu async out\n") + }) + + NULL + }) + + ## Compute visualization plot (async) + ok.ggvisu <- reactiveVal() + observe({ + # if (is.null(ok.visu())) return(NULL) + if (is.null(ok.visu())) { + ok.ggvisu(NULL) + return(NULL) + } + # if (!isTRUE(values$visugo)) return(NULL) + if (!input$panelVisu) return(NULL) + + if (verbose) + cat("DEBUG compute visu plot\n") + ggdat <- data.frame(ok.visu(), tag = values$label) + if (!is.null(ok.predlab1())) + ggdat$tag[is.na(values$label)] <- ok.predlab1()[is.na(values$label)] + da_predlabs <- ok.predlab1() + da_tagged <- ! is.na(values$label) + da_query <- values$newQuery + + ggvisu_async <- future({ + ggres <- ggplot(ggdat, aes(X1, X2)) + theme_bw() + + xlab("") + ylab("") + + theme(legend.position = "bottom") + + scale_shape_manual(values = rep(1:8, 40)) # TODO : adapt to tags + + ## Tagged points + if (any(da_tagged)) { + ggres <- ggres + + geom_point(data = ggdat[da_tagged, ], + aes(X1, X2, color = tag, shape = tag), + size = 1.5, stroke = 1.5, alpha = .8) + } + + ## Not tagged points + if (!is.null(da_predlabs)) { + ggres <- ggres + + geom_point(data = ggdat[!da_tagged, ], + aes(color = tag, shape = tag), show.legend = FALSE) + } else { + ggres <- ggres + + geom_point(alpha = .8, data = ggdat[is.na(ggdat$tag), ]) + } + + ## Current query + if (!is.null(da_query)) { + if (is.null(da_predlabs)) { + ggres <- ggres + + geom_point(data = ggdat[da_query, ], color = 2, + stroke = 3, shape = 3, show.legend = FALSE) + } else { + ggres <- ggres + + geom_point(data = ggdat[da_query, ], + stroke = 3, shape = 3, show.legend = FALSE) + } + } + ggres + }) %...>% ok.ggvisu() + + ggvisu_async <- catch(ggvisu_async, function(error) { + values$visuMsg <- paste0("Error in ggplot: ", error) + }) + + }) + + observe({ + if (verbose) + cat("DEBUG update visuPlot\n") + if (is.null(ok.ggvisu())) { + values$visuMsg <- "No visualization yet." + output$visuPlot <- NULL + values$visuZoom$xlim <- NULL + values$visuZoom$ylim <- NULL + return(NULL) + } + output$visuPlot <- renderPlot(height = input$visuHeight, { + # if (is.null(ok.ggvisu())) return(NULL) + ok.ggvisu() + + coord_fixed(xlim = values$visuZoom$xlim, ylim = values$visuZoom$ylim) + }) + }) + + observeEvent(input$visuDblclick, { + if (!is.null(values$visuMsg)) + values$visuMsg <- NULL + tmp <- input$visuDblclick + bmu <- t(ok.visu()) - c(tmp$x, tmp$y) + bmu <- which.min(colSums(bmu^2)) + values$lastQuery <- bmu + values$newQuery <- bmu + ready2tag(1) + }) + + observeEvent(input$visuGoZoom, { + if (!is.null(values$visuMsg)) + values$visuMsg <- NULL + values$visuZoom$xlim <- c(input$visuBrush$xmin, input$visuBrush$xmax) + values$visuZoom$ylim <- c(input$visuBrush$ymin, input$visuBrush$ymax) + }) + + + ############################################################################## + ## Panel History + ############################################################################## + + ## Create history link and retag events as they appear + observeEvent(input$histDTable_rows_current, { + histrows <- order(values$labelhist) + histrows <- rev(histrows[!is.na(values$labelhist[histrows])]) + histrows <- histrows[!is.na(values$label[histrows])] + histrows <- histrows[input$histDTable_rows_current] + notthere <- histrows[! histrows %in% values$histQueries] + + if (!length(notthere)) return(NULL) + lapply(notthere, function(daquery) { + if (verbose) cat("DEBUG create history events", daquery, "\n") + observeEvent(input[[paste0("hist", daquery)]], { + if (verbose) cat("DEBUG history link", daquery, "\n") + values$lastQuery <- daquery + values$newQuery <- daquery + updateTabsetPanel(session, "mainPanelset", selected = "Tagging") + ready2tag(1) + }) + + observeEvent(input[[paste0("histTag", daquery)]], { + if (verbose) cat("DEBUG history retag", daquery, "\n") + values$histSaveStack <- rbind( + values$histSaveStack, + data.frame(id = ok.data()[daquery, values$conf$idcol], + tag = input[[paste0("histTag", daquery)]], + comment = values$comment[daquery]) + ) + }) + }) + values$histQueries <- c(values$histQueries, notthere) + }) + + ## History save action + observeEvent(input$histSave, { + req(is.data.frame(values$histSaveStack)) + values$tagTrigger <- as.list(values$histSaveStack) + values$label[match(values$histSaveStack$id, ok.data()[[values$conf$idcol]])] <- + values$histSaveStack$tag + values$histSaveStack <- NULL + }) + + ## Update save name with number of unsaved changes + observeEvent(length(values$histSaveStack$id), { + updateActionButton( + session, "histSave", + label = paste0("Save changes (", length(unique(values$histSaveStack$id)),")")) + }) + + ## History table, with clickable links and retagging + output$histDTable <- DT::renderDataTable({ + histrows <- order(values$labelhist) + histrows <- rev(histrows[!is.na(values$labelhist[histrows])]) + histrows <- histrows[!is.na(values$label[histrows])] + + # histrows <- rev(values$queries) + hist_df <- data.frame( + # history = length(histrows):1, + history = values$labelhist[histrows], + id = sapply(histrows, function(irow) { + as.character(actionLink( + paste0("hist", irow), + label = ok.data()[irow, values$conf$idcol], + onclick = paste0('Shiny.onInputChange(\"hist', irow, '\", this.id)'))) + }), + Tag = values$label[histrows], + Retag = sapply(histrows, function(irow) { + paste0( + "" + ) + }), + Comment = values$comment[histrows], + Text = ok.nicetext()[histrows]) + if (!is.null(values$conf$contextcols)) + hist_df <- cbind(hist_df, + ok.data()[histrows, values$conf$contextcols]) + + DT::datatable(hist_df, escape = FALSE, rownames = FALSE, filter = "top", + options = list( + columnDefs = list(list( + targets = 5, + render = DT::JS( + "function(data, type, row, meta) {", + "return type === 'display' && data.length > 200 ?", + "'' + data.substr(0, 200) + '...' : data;", + "}"))) + )) + }) + + ############################################################################## + ## Panel "Stats" + ############################################################################## + + output$statsTagTable <- renderTable(rownames = F, { + table(tag = values$label) + }) + + + ## Model diagnostics by CV + output$statsCVoutput <- reactive({ + if (is.null(values$modelcv)) + return("Model diagnostics will appear here") + values$modelcv + }) + + output$statsCVtable <- DT::renderDataTable({ + if (is.null(values$cvtable)) + return(NULL) + DT::datatable(values$cvtable, list(pageLength = 25), rownames = F) + }) + + ## Button action: compute CV (async) + cv_nclicks <- reactiveVal(0) + observeEvent(input$statsCVgo, { + if (is.null(ok.train())) { + values$modelcv <- "Error in model training" + return(NULL) + } + if (cv_nclicks() != 0) { + values$modelcv <- "CV already running" + return(NULL) + } + cv_nclicks(cv_nclicks() + 1) + + predmat <- ok.predictor()[!is.na(values$label), ] + predlab <- values$label[!is.na(values$label)] + cvsamp <- sample(10, nrow(predmat), replace = TRUE) + cvres <- rep(NA, nrow(predmat)) + cvres_future <- reactiveVal() + + tmp_model <- input$predModel + if (tmp_model == "random forest") { + cv_args <- list( + num.trees = input$rfNumTrees, mtry = if (input$rfMtry > 0) input$rfMtry, + sample.fraction = input$rfSampleFrac, probability = FALSE) + } else if (tmp_model == "linear") { + cv_args <- list(type= 0, cost = input$liblinCost) + } else if (tmp_model == "lasso") { + cv_args <- input$glmLambda + } else if (tmp_model == "naive bayes") { + cv_args <- list(smooth = input$naiveSmooth, prior = input$naivePrior, + distribution = input$naiveDistri) + } else if (tmp_model == "knn") { + cv_args <- list(k = input$knnK) + } + + cv_status_file <- paste0(session$token, "_cv") + + cv_async <- future(seed = TRUE, { + for (icv in 1:10) { + set_status(cv_status_file, paste0("Computing CV fold ", icv, " / 10")) + if (tmp_model == "random forest") { + cvmodel <- do.call(ranger::ranger, c(cv_args, list( + x= predmat[cvsamp != icv, ], + y= as.factor(predlab[cvsamp != icv])))) + cvres[cvsamp == icv] <- as.character( + predict(cvmodel, data= predmat[cvsamp == icv, ])$predictions) + } else { + tmpvalues <- predlab[cvsamp != icv] + # for (ival in na.omit(unique(tmpvalues))) + # if (sum(tmpvalues == ival, na.rm = T) == 1) + # tmpvalues[which(tmpvalues == ival)] <- NA + + if (tmp_model == "lasso") { + cvmodel <- suppressWarnings( + glmnet::glmnet(x= predmat[cvsamp != icv, ][!is.na(tmpvalues), ], + y= as.factor(tmpvalues[!is.na(tmpvalues)]), + family= "multinomial")) + cvres[cvsamp == icv] <- predict( + cvmodel, newx = predmat[cvsamp == icv, ], + type = "class", s= cv_args) + } else if (tmp_model == "linear") { + cvmodel <- do.call(LiblineaR::LiblineaR, c( + list(data= predmat[cvsamp != icv, ][!is.na(tmpvalues), ], + target= as.factor(tmpvalues[!is.na(tmpvalues)])), + cv_args)) + cvres[cvsamp == icv] <- as.character( + predict(cvmodel, newx= predmat[cvsamp == icv, ])$predictions) + } else if (tmp_model == "naive bayes") { + cvmodel <- do.call(quanteda.textmodels::textmodel_nb, c( + list(x= predmat[cvsamp != icv, ][!is.na(tmpvalues), ], + y= as.factor(tmpvalues[!is.na(tmpvalues)])), + cv_args)) + cvres[cvsamp == icv] <- as.character( + predict( + cvmodel, newdata= predmat[cvsamp == icv, ], type= "class")) + } else if (isolate(tmp_model) == "knn") { + cvres[cvsamp == icv] <- + as.character(do.call(class::knn, c(cv_args, list( + train = predmat[cvsamp != icv, ], + test = predmat[cvsamp == icv, ], + cl = predlab[cvsamp != icv])))) + } + } + } + cvres + }) %...>% cvres_future() + + cv_async <- catch(cv_async, function(e) { + cvres_future(NULL) + values$modelcv <- e$message + }) + + cv_async <- finally(cv_async, function() { + cv_nclicks(0) + if (file.exists(cv_status_file)) unlink(cv_status_file) + if (is.null(cvres_future())) return(NULL) + cvres <- cvres_future() + predlab <- values$label[!is.na(values$label)] + dametrics <- metricsTable(predlab, cvres) + values$cvtable <- dametrics + res <- "10-CV OOB evaluation:\n" + # if (any(grepl("^bertpred", input$use_regressors))) + if (any(grepl("^bertpred", values$new_use_regressors))) + res <- paste0( + res, + "⚠️ Scores are biased upwards if BERT predictions were trained on current data ⚠️ \n") + res <- paste0( + res, + "* Accuracy : ", round(100 * mean(cvres == predlab), 2), "%\n", + "* Macro F1 : ", sprintf("%f", mean(as.numeric(dametrics$F1))), "\n", + "* Weighted F1 : ", sprintf("%f", weighted.mean(as.numeric(dametrics$F1), + dametrics$N.cases)), "\n" + ) + values$modelcv <- res + }) + + NULL + }) + + ## Automatic message update during CV computation + observe({ + if (cv_nclicks() == 0) return(NULL) + invalidateLater(200) + if (!file.exists(paste0(session$token, "_cv"))) return(NULL) + values$modelcv <- get_status(paste0(session$token, "_cv")) + values$cvtable <- NULL + }) + + + ############################################################################## + ## Panel BERT + ############################################################################## + + ########## + ## Bert UI left panel + + ## Update list of saved models and dlSelect on init and on saveTrigger + + observeEvent(list(ok.data(), values$bertSaveTrigger), { + damodels <- dir(pattern = "^tiggerbert_saved_") + if (!length(damodels)) return(NULL) + damodels <- gsub("^tiggerbert_saved_", "", damodels) + values$bertSaved <- damodels + updateSelectInput(session, "dlBPSelect", choices = damodels) + }) + + ## Populate saved models list, with buttons + + output$bertSaved <- renderUI({ + if (verbose) + cat("DEBUG bert update saved list\n") + HTML(paste0( + "", + paste0(sapply(values$bertSaved, function(dabert) { + bertshort <- dabert + if (nchar(bertshort) > 23) + bertshort <- paste0(substr(bertshort, 1, 20), "...") + paste0( + "") + }), collapse = "\n"), + "
    ", + actionLink(paste0("saved_", dabert), bertshort, title = dabert, + style="margin-right: 5px;"), "", + actionButton(paste0("bert_copypars_", dabert), "📋", + title = "Copy parameters"), "", + actionButton(paste0("bert_infer_", dabert), "🤖", + title = "Infer predictions on current data"), "", + actionButton(paste0("bert_delete_", dabert), "🗑", + title = "Delete model"), + "
    " + )) + }) + + ########## + ## Bert UI main panel + + ## UI outputs : message and stats table and plot + output$bertMsg <- reactive( + ifelse(is.null(values$bertMsg), + "Train new BERT, or select saved model.", + values$bertMsg)) + + output$bertValstats <- DT::renderDataTable( + DT::datatable(values$bertValstats, list(pageLength = 25))) + + output$bertValPlot <- renderPlot(values$bertValPlot) + + output$bertMsgHyperpar <- reactive(values$bertMsgHyperpar) + + ################################## + ## Async handling of BERT training + + ## Status File + bert_status_file <- paste0("tiggerbert_", session$token, "_trainstatus") + + fire_interrupt <- function(dafile) set_status(dafile, "interrupt") + fire_ready <- function(dafile) set_status(dafile, "Ready") + fire_running <- function(dafile, perc_complete = NULL){ + if(is.null(perc_complete)) + msg <- "Training..." + else + msg <- paste0("Training... ", perc_complete, "% complete") + set_status(dafile, msg) + } + interrupted <- function(dafile) get_status(dafile) == "interrupt" + + ## Create Status File + fire_ready(bert_status_file) + + + ## Button action preparation + bert_nclicks <- reactiveVal(0) + bert_result_val <- reactiveVal() + bert_hyperpars <- reactiveVal() + + ## Button appearance: write "Stop" if training is ongoing + observeEvent(bert_nclicks(), { + updateActionButton(session, "bertTrain", + label = ifelse(bert_nclicks() > 0, "Stop", "Train BERT")) + }) + + ## Button action: Train new BERT + observeEvent(input$bertTrain,{ + + ## Don't do anything if analysis is already being run + # if(bert_nclicks() != 0){ + # values$bertMsg <- "BERT is already training" + # return(NULL) + # } + tmpsession <- paste0("tiggerbert_", ok.labelcol(), "_", session$token) + if(bert_nclicks() != 0){ + write("stop", paste0(tmpsession, "_stop")) + bert_nclicks(0) + return(NULL) + } + if (file.exists(paste0(tmpsession, "_stop"))) + file.remove(paste0(tmpsession, "_stop")) + + ## Increment clicks and prevent concurrent analyses + bert_nclicks(bert_nclicks() + 1) + + bert_result_val(data.frame(Status="Running...")) + fire_running(bert_status_file) + + ## Prepare async training + values$bertLoadSaved <- NULL + # tmpsession <- paste0("tiggerbert_", ok.labelcol(), "_", session$token) + tmpmodel <- input$bertModel + system(paste0("rm ", tmpsession, "_done")) + + tmpdat <- data.frame( + id = ok.data()[, values$conf$idcol], + text = ok.nicetext(), + label = values$label + ) + tmpdat <- tmpdat[!is.na(tmpdat$label), ] + tmpdat <- tmpdat[grepl("\\S", tmpdat$text), ] + + ## Filter data: min occurences, balance classes, validation split + tmplabtable <- table(tmpdat$label) + if (is.numeric(input$bertMinOccur)) if (input$bertMinOccur > 1) { + tmpdat <- tmpdat[tmpdat$label %in% + names(which(tmplabtable >= input$bertMinOccur)), ] + tmplabtable <- table(tmpdat$label) + } + set.seed(input$bertValidSeed) + if (input$bertBalance) { + tmpminocc <- min(tmplabtable) + tmptrain <- sample(do.call(c, lapply(unique(tmpdat$label), function(dalabel) + sample(which(tmpdat$label == dalabel), round((1-input$bertValidFrac) * tmpminocc))))) + tmpvalid <- sample(do.call(c, lapply(unique(tmpdat$label), function(dalabel) { + dasamp <- which(tmpdat$label == dalabel) + dasamp <- dasamp[! dasamp %in% tmptrain] + sample(dasamp, round(input$bertValidFrac * tmpminocc)) + }))) + } else { + tmptrain <- sample(1:nrow(tmpdat), round((1 - input$bertValidFrac) * nrow(tmpdat))) + tmpvalid <- (1:nrow(tmpdat))[-tmptrain] + } + + write.csv(tmpdat[tmptrain, ], paste0(tmpsession, "_dat_train.csv"), row.names = FALSE) + write.csv(tmpdat[tmpvalid, ], paste0(tmpsession, "_dat_valid.csv"), row.names = FALSE) + + iwait <- 0 + while (iwait < 10) { + if (!file.exists(paste0(tmpsession, "_dat_valid.csv"))) Sys.sleep(500) + iwait <- iwait + 1 + } + + tmp_cmd <- paste0( + values$conf$python, " gobert.py -m ", tmpmodel, + " -t ", tmpsession, "_dat_train.csv", + " -v ", tmpsession, "_dat_valid.csv", + " -b ", input$bertBatchsize, + " -g ", input$bertGradacc, + " -e ", input$bertEpochs, + " -l ", input$bertLrate, + " -w ", input$bertWdecay, + " -B ", ifelse(input$bertBest, 1, 0), + " -E ", input$bertNeval, + " -s ", tmpsession, + " -G ", as.numeric(values$conf$use_gpu), + " -A ", as.numeric(input$bertAdapt), + " 2>&1 ; touch ", tmpsession, "_done" + ) + + ## Save hyperpars + bert_hyperpars(list(bertModel = input$bertModel, + bertEpochs = input$bertEpochs, + bertLrate = input$bertLrate, + bertWdecay = input$bertWdecay, + bertBatchsize = input$bertBatchsize, + bertGradacc = input$bertGradacc, + bertNeval = input$bertNeval, + bertBest = input$bertBest, + bertValidFrac = input$bertValidFrac, + bertValidSeed = input$bertValidSeed, + bertNtrain = length(tmptrain), + bertTrainTable = as.list(table(tmpdat$label[tmptrain])), + bertScheme = ok.labelcol())) + + ## Launch async training + bert_async <- future({ + if (file.exists(tmpsession)) + system(paste0("rm -R ", tmpsession, "*")) + + # system(tmp_cmd, wait = FALSE, intern = FALSE) + tmp_exec <- system(tmp_cmd, wait = FALSE, intern = TRUE) + writeLines(tmp_exec, paste0(tmpsession, "_log")) + + ## After training, get trainer state from last checkpoint for diagnostics + chkpt <- dir(paste0(tmpsession, "_train"), pattern = "^checkpoint") + chkpt <- + chkpt[order(as.numeric(gsub("\\D", "", chkpt)), decreasing = T)[1]] + system(paste0( + "cp ", tmpsession, "_train/", chkpt, "/trainer_state.json ", + tmpsession, "/")) + }) %...>% bert_result_val() + + ## Catch interrupt (or any other error) and notify user + bert_async <- catch(bert_async, function(e){ + bert_result_val(NULL) + showNotification(e$message) + values$bertMsg <- e$message + }) + + ## After the promise has been evaluated set nclicks to 0 to allow another Run + bert_async <- finally(bert_async, function() { + system(paste0("rm -R ", tmpsession, "_train")) + system(paste0("rm ", tmpsession, "_done")) + + fire_ready(bert_status_file) + bert_nclicks(0) + + if (is.null(bert_result_val())) return(NULL) + + bertpreds <- try(read.csv(paste0(tmpsession, "_predval.csv"))) + if (inherits(bertpreds, "try-error")) { + # values$bertMsg <- "BERT error - try again" + values$bertMsg <- try(paste(readLines(paste0(tmpsession, "_log")), collapse = "\n")) + values$bertValstats <- NULL + values$bertValPlot <- NULL + values$bertLastUnsaved <- NULL + values$bertMsgHyperpar <- NULL + values$bertLastHyperpar <- NULL + return(NULL) + } + + datruth <- tmpdat[tmpvalid, "label"] + dapred <- bertpreds$bertpred + dametrics <- metricsTable(datruth, dapred) + damsg <- paste0( + "Unsaved model (", tmpmodel, ")\n\n", + "BERT validation stats:\n", + "* Accuracy : ", round(100 * mean(datruth == dapred), 2), "%\n", + "* Macro F1 : ", sprintf("%f", mean(as.numeric(dametrics$F1))), "\n", + "* Weighted F1 : ", sprintf("%f", weighted.mean(as.numeric(dametrics$F1), + dametrics$N.cases)), "\n") + hypermsg <- bertHyperToMsg(bert_hyperpars()) + writeLines(RJSONIO::toJSON(bert_hyperpars()), + paste0(tmpsession, "/tiggertrain.json")) + + values$bertValstats <- dametrics + values$bertMsg <- damsg + values$bertValPlot <- bertPlotFromPath(tmpsession) + values$bertLastUnsaved <- list( + metrics = dametrics, msg = damsg, session = tmpsession) + values$bertMsgHyperpar <- hypermsg + values$bertLastHyperpar <- bert_hyperpars() + }) + + values$bertMsg <- "BERT is training" + values$bertValstats <- NULL + values$bertValPlot <- NULL + values$bertLastUnsaved <- NULL + values$bertMsgHyperpar <- NULL + values$bertLastHyperpar <- NULL + + ## Return something other than the promise so shiny remains responsive + NULL + }) + + ## Automatic check of BERT training status + observe({ + if (bert_nclicks() == 0) return(NULL) + isolate({ + tmpsession <- paste0("tiggerbert_", ok.labelcol(), "_", session$token) + ## Check for checkpoint dirs + checkdirs <- + length(dir(paste0(tmpsession, "_train"), pattern = "^checkpoint")) + # fire_running(bert_status_file, + # min(100, round(100 * checkdirs / input$bertNeval, 1))) + # dastate <- try(RJSONIO::fromJSON(paste0(tmpsession, "_train/trainer_state.json"))) + # if (!inherits(dastate, "try-error")) + # fire_running(bert_status_file, + # round(100 * as.numeric(dastate$epoch) / input$bertEpochs, 1)) + try({ + dapoints <- dir(paste0(tmpsession, "_train"), + pattern = "trainer_state[.]json$", recursive = T) + dapoints_num <- as.numeric(gsub("^checkpoint-(\\d+)/.*$", "\\1", dapoints)) + statefile <- paste0( + tmpsession, "_train/", dapoints[which.max(dapoints_num)]) + if (file.exists(statefile)) { + state <- RJSONIO::fromJSON(statefile) + fire_running(bert_status_file, + round(100 * as.numeric(state$epoch) / input$bertEpochs, 1)) + } + }) + }) + if (checkdirs < isolate(input$bertNeval) & file.exists(paste0(tmpsession, "_done"))) { + values$bertMsg <- "BERT Error - try again" + # values$bertMsg <- paste(readLines(paste0(tmpsession, "_log")), collapse = "\n") + } else { + invalidateLater(1e3) + loss_progress <- "" + if (checkdirs > 0) { + ## Get training diagnostics + try({ + dapoints <- dir(paste0(tmpsession, "_train"), + pattern = "trainer_state[.]json$", recursive = T) + dapoints_num <- as.numeric(gsub("^checkpoint-(\\d+)/.*$", "\\1", dapoints)) + statefile <- paste0( + tmpsession, "_train/", dapoints[which.max(dapoints_num)]) + if (file.exists(statefile)) { + state <- RJSONIO::fromJSON(statefile) + logs <- data.table::rbindlist(lapply(state$log_history, as.list), + fill = TRUE) + logs <- logs[, .(loss = max(loss, na.rm = T), + eval_loss = max(eval_loss, na.rm = T)), by = epoch] + loss_progress <- paste0( + "\n* Epoch : ", sprintf("%.02f", logs$epoch), + "\ttrain loss : ", sprintf("%.05f",logs$loss), + "\teval loss : ", sprintf("%.05f", logs$eval_loss), collapse = "") + } + }) + } + values$bertMsg <- + ifelse(checkdirs == 0, "Pre-processing...", + paste0(get_status(bert_status_file), loss_progress)) + } + values$bertValstats <- NULL + values$bertValPlot <- NULL + values$bertLoadSaved <- NULL + values$bertMsgHyperpar <- NULL + }) + + + ## Action: show last BERT model + observeEvent(input$bertLast, { + values$bertMsg <- values$bertLastUnsaved$msg + values$bertValstats <- values$bertLastUnsaved$metrics + values$bertValPlot <- bertPlotFromPath(values$bertLastUnsaved$session) + values$bertMsgHyperpar <- bertHyperToMsg(values$bertLastHyperpar) + values$bertLoadSaved <- NULL + }) + + + ## Action: save BERT model + observeEvent(input$bertSave, { + tmpsession <- paste0("tiggerbert_", ok.labelcol(), "_", session$token) + if (!file.exists(paste0(tmpsession))) + return(NULL) + + if (!grepl("\\S", input$bertSaveName)) { + tmpname <- paste0( + ok.labelcol(), "_", gsub("\\D+$", "", gsub("\\D", "-", Sys.time()))) + } else + tmpname <- cleanFileName(input$bertSaveName) + + savedir <- paste0("tiggerbert_saved_", tmpname) + system(paste("cp -R", tmpsession, savedir)) + system(paste0("cp ", tmpsession, "_dat_valid.csv ", + savedir, "/tiggerbert_dat_valid.csv")) + system(paste0("cp ", tmpsession, "_predval.csv ", + savedir, "/tiggerbert_pred_valid.csv")) + + system(paste0("rm -R ", tmpsession, "*")) + values$bertMsg <- paste0("BERT model successfully saved: ", tmpname) + values$bertValstats <- NULL + values$bertValPlot <- NULL + values$bertMsgHyperpar <- NULL + + if (is.null(values$bertSaveTrigger)) { + values$bertSaveTrigger <- 0 + } else + values$bertSaveTrigger <- values$bertSaveTrigger + 1 + values$bertLastUnsaved <- NULL + updateTextInput(session, "bertSaveName", value = "") + }) + + + ## Actions: load saved model (list updated only with new models) + observeEvent(values$bertSaved, { + if (!length(values$bertSaved)) return(NULL) + tmpsaved <- values$bertSaved[! values$bertSaved %in% values$bertSavedAll] + + lapply(tmpsaved, function(dabert) { + ############ + ## Actions to load saved model + observeEvent(input[[paste0("saved_", dabert)]], { + bertconf <- RJSONIO::fromJSON(paste0( + "tiggerbert_saved_", dabert, "/config.json")) + truth <- read.csv(paste0( + "tiggerbert_saved_", dabert, "/tiggerbert_dat_valid.csv"))$label + pred <- read.csv(paste0( + "tiggerbert_saved_", dabert, "/tiggerbert_pred_valid.csv"))$bertpred + + dametrics <- metricsTable(truth, pred) + values$bertValstats <- dametrics + values$bertMsg <- paste0( + "Saved model ", dabert, " (", bertconf[["_name_or_path"]], ")\n\n", + "BERT validation stats:\n", + "* Accuracy : ", round(100 * mean(truth == pred), 2), "%\n", + "* Macro F1 : ", sprintf("%f", mean(as.numeric(dametrics$F1))), "\n", + "* Weighted F1 : ", sprintf("%f", weighted.mean(as.numeric(dametrics$F1), + dametrics$N.cases)), "\n") + + ## Hyperparameters + tiggertrain_file <- paste0("tiggerbert_saved_", dabert, "/tiggertrain.json") + if (file.exists(tiggertrain_file)) { + try({ + values$bertMsgHyperpar <- bertHyperToMsg(RJSONIO::fromJSON(tiggertrain_file)) + }) + } else + values$bertMsgHyperpar <- "Training parameters not saved." + + values$bertValPlot <- + bertPlotFromPath(paste0("tiggerbert_saved_", dabert)) + values$bertLoadSaved <- dabert + }) + + ############ + ## Actions to copy hyperparameters + observeEvent(input[[paste0("bert_copypars_", dabert)]], { + tiggertrain_file <- paste0("tiggerbert_saved_", dabert, "/tiggertrain.json") + if (!file.exists(tiggertrain_file)) return(NULL) + try({ + tmppars <- RJSONIO::fromJSON(tiggertrain_file) + updateSelectInput(session, "bertModel", selected = tmppars$bertModel) + updateCheckboxInput(session, "bertBest", value = tmppars$bertBest) + numinputs <- c("bertEpochs", "bertLrate", "bertWdecay", + "bertBatchsize", "bertGradacc") + for (iinput in numinputs) + updateNumericInput(session, iinput, value = tmppars[[iinput]]) + }) + }) + + ############ + ## Actions to delete saved model (with confirm) + observeEvent(input[[paste0("bert_delete_", dabert)]], { + showModal(modalDialog( + title = "Delete BERT model", + paste0("Confirm delete: ", dabert, + " (model and predictions will be deleted)."), + footer = tagList(actionButton(paste0("bert_delete_confirm_", dabert), "Delete"), + modalButton("Cancel")))) + }) + + observeEvent(input[[paste0("bert_delete_confirm_", dabert)]], { + system(paste0("rm -R tiggerbert_saved_", dabert)) + system(paste0( + "rm ", values$conf$datadir, values$conf$dataname, "_bertpred_", + dabert, ".feather")) + # values$bertLoadSaved <- NULL + if (is.null(values$bertSaveTrigger)) { + values$bertSaveTrigger <- 0 + } else + values$bertSaveTrigger <- values$bertSaveTrigger + 1 + removeModal() + values$bertMsg <- NULL + values$bertValstats <- NULL + values$bertMsgHyperpar <- NULL + }) + + ############ + ## Actions to infer on current data from saved BERT (async) + observeEvent(input[[paste0("bert_infer_", dabert)]], { + if (bertpred_nclicks() > 0 | bpall_nclicks() > 0) { + values$bertMsg <- "Inference already running" + values$bertMsgHyperpar <- NULL + values$bertValPlot <- NULL + values$bertValstats <- NULL + return(NULL) + } + bertpred_nclicks(1) + + saved_model <- paste0("tiggerbert_saved_", dabert) + out_path <- paste0( + values$conf$datadir, values$conf$dataname, "_bertpred_", + dabert, ".feather") + + if (file.exists(out_path)) { + old_infer <- arrow::read_feather(out_path) + } else + old_infer <- NULL + + to_infer <- ok.data()[[values$conf$idcol]] + to_infer <- to_infer[! to_infer %in% old_infer[[values$conf$idcol]]] + to_infer_row <- match(to_infer, ok.data()[[values$conf$idcol]]) + + if (!length(to_infer)) { + values$bertMsg <- paste0( + "All current ", nrow(ok.data()), + " texts already infered by ", dabert) + values$bertMsgHyperpar <- NULL + values$bertValPlot <- NULL + values$bertValstats <- NULL + bertpred_nclicks(0) + return(NULL) + } + + values$bertValPlot <- NULL + values$bertValstats <- NULL + values$bertMsgHyperpar <- NULL + values$bertMsg <- paste0( + "Preparing inference on ", length(to_infer), " new texts") + tmp_dat <- paste0("tiggerbert_infer_", session$token, ".feather") + arrow::write_feather( + data.frame( + ok.data()[to_infer_row, values$conf$idcol, drop = FALSE], + text = ok.nicetext()[to_infer_row]), + tmp_dat) + + values$bertMsg <- paste0( + "Infering ", dabert, " predictions on ", + length(to_infer), " new texts") + infer_cmd <- paste0( + values$conf$python, " gobert_infer.py -m ", saved_model, + " -o ", out_path, + " -d ", tmp_dat, + " -i ", values$conf$idcol, + " -l ", paste0("tiggerbert_infer_", session$token, ".log"), + " -G ", as.numeric(values$conf$use_gpu), + " -b ", 128) + bertpred_res <- reactiveVal() + bertpred_async <- future({ + system(infer_cmd) + system(paste0("rm ", tmp_dat)) + + if (!is.null(old_infer)) { + new_infer <- arrow::read_feather(out_path) + arrow::write_feather(rbind(old_infer, new_infer), out_path) + } + }) %...>% bertpred_res + bertpred_async <- catch(bertpred_async, function(error) { + bertpred_nclicks(0) + values$bertMsg <- paste0("BERT inference error: ", error) + system(paste0("rm ", tmp_dat)) + }) + bertpred_async <- finally(bertpred_async, function() { + bertpred_nclicks(0) + values$bertMsg <- + "Inference complete!\nRe-import data in Project/Sample panel to use the predictions for tagging." + values$bertValPlot <- NULL + values$bertValstats <- NULL + values$bertMsgHyperpar <- NULL + }) + NULL + }) + }) + values$bertSavedAll <- c(values$bertSavedAll, tmpsaved) + }) + + ## Automatic check of BERT inference progress + observe({ + if (bertpred_nclicks() == 0) return(NULL) + invalidateLater(1e3) + logfile <- paste0("tiggerbert_infer_", isolate(session$token), ".log") + if (file.exists(logfile)) { + values$bertMsg <- readLines(logfile)[1] + values$bertValPlot <- NULL + values$bertValstats <- NULL + values$bertMsgHyperpar <- NULL + } + }) + + + + + ############################################################################## + ## Export panel + ############################################################################## + + ## Export tags and full model predictions + output$dlTagSave <- + downloadHandler( + function() + paste0(Sys.Date(), "-activeTigger-", ok.labelcol(),".", input$dlTagFormat), + function(con) { + if (is.null(ok.data())) return(NULL) + dadat <- ok.data()[, values$conf$idcol, drop = FALSE] + if ("tags" %in% input$dlTagSelect) + dadat[[ok.labelcol()]] <- values$label + if ("comments" %in% input$dlTagSelect) if (ok.commentCol() != "(None)") + dadat[, ok.commentCol()] <- values$comment + if ("predictions" %in% input$dlTagSelect) if (ok.train.possible()) { + dadat$tigger.predict <- ok.predlab1() + dadat$tigger.predprob <- apply(ok.pred(), 1, max) + tmp_pred <- ok.pred() + colnames(tmp_pred) <- paste0("tiggerpred_", colnames(tmp_pred)) + dadat <- cbind(dadat, tmp_pred) + } + othercols <- input$dlTagSelect + othercols <- othercols[! othercols %in% c("tags", "comments", "predictions")] + if (length(othercols)) + dadat <- cbind(dadat, ok.data()[, othercols, drop = FALSE]) + + if (input$dlTagFormat == "csv") { + write.csv(dadat, row.names = F, con) + } else + arrow::write_feather(dadat, con) + }) + + + ## Export embeddings (ft, sb, umap, t-sne) + output$dlEmbedSave <- downloadHandler( + function() + paste0(Sys.Date(), "-activeTigger-emb-", + paste(input$dlEmbedSelect, collapse = "-"), + ".", input$dlEmbedFormat), + function (file) { + if (is.null(input$dlEmbedSelect)) return(NULL) + export <- ok.data()[, values$conf$idcol, drop = FALSE] + if ("ft" %in% input$dlEmbedSelect) + export <- data.frame(export, ok.data()[, values$cols_ft]) + if ("sb" %in% input$dlEmbedSelect) + export <- data.frame(export, ok.data()[, values$cols_sb]) + if (any(c("t-sne", "umap") %in% input$dlEmbedSelect)) { + tmpvisu <- ok.visu() + tmpalgo <- input$dlEmbedSelect[input$dlEmbedSelect %in% c("t-sne", "umap")] + colnames(tmpvisu) <- paste0(gsub("-", "", tmpalgo), 1:2) + export <- data.frame(export, tmpvisu) + } + if (input$dlEmbedFormat == "csv") { + write.csv(export, file, row.names = FALSE) + } else if (input$dlEmbedFormat == "feather") { + arrow::write_feather(export, file) + } + }) + + ## Export BERT predictions + + output$dlBPMsg <- reactive(values$dlBPMsg) + + + + # output$dlBertPredCsv <- + # downloadHandler(paste0(Sys.Date(), "-bertpred-", ok.labelcol(),".csv"), + # content= function(con) { + # if (is.null(ok.data())) return(NULL) + # dadat <- ok.data()[, c(values$conf$idcol, ok.labelcol())] + # dadat[[ok.labelcol()]] <- values$label + # if (ok.commentCol() != "(None)") { + # dadat[, ok.commentCol()] <- values$comment + # } + # if (ok.train.possible()) { + # dadat$tigger.predict <- ok.predlab1() + # dadat$tigger.predprob <- apply(ok.pred(), 1, max) + # } + # dadat <- cbind(dadat, ok.pred()) + # write.csv(dadat, row.names = F, con) + # }) + + ############ + ## Action to infer on ALL data from saved BERT (async) + bpall_res <- reactiveVal() + observeEvent(input$dlBPInfer, { + if (bpall_nclicks() > 0 | bertpred_nclicks() > 0) { + values$dlBPMsg <- "Inference already running" + bpall_res(NULL) + return(NULL) + } + bpall_nclicks(1) + bpall_res_async <- reactiveVal() + dabert <- input$dlBPSelect + + saved_model <- paste0("tiggerbert_saved_", dabert) + out_path <- paste0( + values$conf$datadir, values$conf$dataname, "_bertpredall_", + dabert, ".feather") + tmp_dat <- paste0("tiggerbert_inferall_", session$token, ".feather") + tmp_log <- paste0("tiggerbert_inferall_", session$token, ".log") + tmp_done <- paste0("tiggerbert_inferall_", session$token, "_done") + if (file.exists(tmp_done)) + system(paste0("rm ", tmp_done)) + + if (file.exists(out_path)) { + values$dlBPMsg <- "Inference already complete, ready for download" + bpall_nclicks(0) + bpall_res(out_path) + return(NULL) + # old_infer <- arrow::read_feather(out_path) + } + + fulldat <- arrow::read_feather(paste0( + values$conf$datadir, values$conf$dataname, ".feather")) + values$dlBPMsg <- paste0( + "Preparing inference on ", nrow(fulldat), " texts") + if (length(values$conf$textcols) > 1) { + fulldat$text <- do.call(paste, c( + fulldat[, values$conf$textcols, drop = FALSE], sep = "\n\n")) + } else { + fulldat$text <- fulldat[, values$conf$textcols] + } + arrow::write_feather(fulldat[, c(values$conf$idcol, "text")], tmp_dat) + + infer_cmd <- paste0( + values$conf$python, " gobert_infer.py -m ", saved_model, + " -o ", out_path, + " -d ", tmp_dat, + " -i ", values$conf$idcol, + " -l ", tmp_log, + " -G ", as.numeric(values$conf$use_gpu), + " -b ", 128, + "; touch ", tmp_done) + + bpall_async <- future({ + system(infer_cmd) + system(paste0("rm ", tmp_dat)) + }) %...>% bpall_res_async + + bpall_async <- catch(bpall_async, function(error) { + values$dlBPMsg <- paste0("BERT inference error: ", error) + system(paste0("rm ", tmp_dat)) + system(paste0("rm ", tmp_done)) + }) + + bpall_async <- finally(bpall_async, function() { + bpall_nclicks(0) + values$dlBPMsg <- + "Inference complete, ready for download" + bpall_res(out_path) + }) + NULL + }) + + ## Automatic check of BERT all prediction progress + observe({ + if (bpall_nclicks() == 0) return(NULL) + invalidateLater(1e3) + logfile <- paste0("tiggerbert_inferall_", isolate(session$token), ".log") + if (file.exists(logfile)) + values$dlBPMsg <- readLines(logfile)[1] + }) + + ## Show BERTpred download button when data is ready + output$dlBPDlButton <- renderUI({ + if (is.null(bpall_res())) return(NULL) + downloadButton("dlBPDl", NULL) + }) + + output$dlBPDl <- downloadHandler( + function() + paste0(Sys.Date(), "-tiggerbert-", values$conf$dataname, + input$dlBPSelect, ".", input$dlBPFormat), + function(file) { + if (is.null(bpall_res())) return(NULL) + tmp_bert <- gsub("^.*_bertpredall_(.*)[.]feather$", "\\1", bpall_res()) + if (tmp_bert != input$dlBPSelect) { + values$dlBPMsg <- "BERT model changed - predict again or select current model" + return(NULL) + } + tmp <- arrow::read_feather(bpall_res()) + if (input$dlBPFormat == "csv") { + write.csv(tmp, file, row.names = FALSE) + } else if (input$dlBPFormat == "feather") { + arrow::write_feather(tmp, file) + } + } + ) + + + ############################################################################## + ## Delete files at end of session + onStop(function() { + if(file.exists(bert_status_file)) unlink(bert_status_file) + if(length(dir(pattern = paste(session$token)))) + system(paste0("rm -R *", session$token, "*")) + }) + +}) diff --git a/docker-images-datalab/activetigger/activetigger/tokenize_spacy.py b/docker-images-datalab/activetigger/activetigger/tokenize_spacy.py new file mode 100644 index 0000000..a7143ce --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/tokenize_spacy.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# coding: utf-8 + +## Spacy tokenize texts +## Requires data file with columns id and text + +import argparse +from os.path import expanduser +import pandas as pd +import pyarrow as pa +import pyarrow.feather as feather +import spacy +import re + + +def main(args): + print("Tokenizer: Importing data") + datapath = expanduser(args.data) + dat = feather.read_feather(datapath) + outfile = re.sub("[.]feather$", "_spa.feather", datapath) + + print("Tokenizer: Loading model") + spa = spacy.load(expanduser(args.model)) + print("Tokenizer: Tokenizing sentences") + tok = [" ".join([str(token) for token in spa.tokenizer(text)]) for text in dat["text"]] + + print("Tokenizer: Exporting") + tok = pd.concat([dat["id"], pd.DataFrame(tok)], axis=1) + tok.columns = ["id", "text_spa"] + feather.write_feather(tok, outfile) + print("Tokenizer: Done") + + +if __name__ == "__main__": + argParser = argparse.ArgumentParser() + argParser.add_argument("-m", "--model", help="Model name", default="fr_core_news_sm") + argParser.add_argument("-d", "--data", help="Path to data (feather)") + args = argParser.parse_args() + main(args) + diff --git a/docker-images-datalab/activetigger/activetigger/ui.R b/docker-images-datalab/activetigger/activetigger/ui.R new file mode 100644 index 0000000..5f788a2 --- /dev/null +++ b/docker-images-datalab/activetigger/activetigger/ui.R @@ -0,0 +1,631 @@ +## 21/04/2020 : shiny pour active learning étiquetage de textes + +shinyUI(fluidPage( + title = "Active Tigger", + + ############################################################################ + ## Top panel: Title, strategy, model options + ############################################################################ + fluidRow( + column( + 4, + fluidRow( + column(4, br(), + HTML('')), + column( + 8, br(), + p(strong("Project / Scheme")), + fluidRow( + column(2, HTML(paste0( + '
    ', + actionButton("createProject", "+"), + '
    '))), + column(10, selectInput("selectProject", NULL, NULL, NULL)) + ), + fluidRow( + column(2, HTML(paste0( + '
    ', + actionButton("createScheme", "+"), + '
    '))), + column(10, selectInput("selectScheme", NULL, NULL, NULL)) + ) + )) + ), + column( + 2, br(), + + HTML(paste0( + '
    ', + p(strong("Strategy")), + selectInput("strategy", NULL, + choices= c("Active" = "entropy", + "MaxProb" = "maxprob", + "Random" = "random", + "Deterministic" = "sequential"), + selected = "sequential"), + conditionalPanel("input.strategy == 'maxprob'", + selectInput("maxprobWhich", NULL, NULL)), + '
    ')) + ), + column( + 2, br(), + HTML(paste0( + '
    ', + p(strong("On")), + selectInput("sampleChoice", NULL, + choices = c("Untagged" = "untagged", + "Tagged" = "tagged", + "All" = "all")), + conditionalPanel("input.sampleChoice == 'tagged'", + selectInput("taggedWhich", NULL, NULL)), + '
    ')) + ), + + column(4, p(br()), + verbatimTextOutput("trainDiagno"), + fluidRow( + column(8, HTML(paste0( + '
    ', + actionButton("modelTrain", "🤖 Train", width = "100%"), + '
    '))), + column(4, HTML(paste0( + '
    ', + checkboxInput("showTrainOptions", "🔧"), + '
    '))) + )) + ), + + + ## General training options + conditionalPanel( + "input.showTrainOptions", + hr(), + fluidRow( + column( + 4, + conditionalPanel( + "input.showTrainOptions & input.use_regressors.includes('regex')", + uiOutput("panelExtraRegex") + ) + ), + column( + 4, + p(strong("Predictors")), + selectizeInput("use_regressors", NULL, "", + multiple = TRUE, width = "100%"), + conditionalPanel( + "input.showTrainOptions & input.use_regressors.includes('extra')", + selectizeInput("use_ootregnum", "Extra predictors: continuous", "", + multiple = TRUE, width = "100%"), + selectizeInput("use_ootregcat", "Extra predictors: categorical", "", + multiple = TRUE, width = "100%") + ), + + conditionalPanel( + "input.showTrainOptions & input.use_regressors.includes('dfm')", + hr(), + fluidRow( + column( + 6, numericInput("dfmMinTermfreq", "DFM : Min Termfreq", + min= 1, max= 1e3, value= 5, step= 1)), + column( + 6, numericInput("dfmMinDocfreq", "DFM : Min Docfreq", + min= 1, max= 1e3, value= 5, step= 1))), + fluidRow( + column( + 4, checkboxInput("dfmTfIdf", "Tf-Idf", TRUE)), + column( + 4, selectInput("dfmTfScheme", label = NULL, + choices= c("logcount", "count", "prop", "propmax", + "boolean", "augmented", "logave"), + selected= "logcount")), + column( + 4, selectInput("dfmDfScheme", label = NULL, + choices= c("inverse", "count", + "inversemax", "inverseprob", "unary"), + selected= "inverse")) + ), + numericInput("dfmNgrams", "N-grams", value= 1, + min= 1, max= 10, step = 1) + ) + ), + column( + 4, + HTML(paste0( + '
    ', + fluidRow( + column(6, strong("Auto train every")), + column(6, numericInput("trainCountdown", NULL, 0, 0, 1e6, 1))), + '
    ' + )), + HTML(paste0( + '
    ', + fluidRow( + column(6, strong("Model")), + column(6, selectInput("predModel", NULL, selected = "linear", + choices = c("Naive Bayes" = "naive bayes", + "KNN" = "knn", + "Liblinear" = "linear", + "LASSO" = "lasso", + "Random Forest" = "random forest"))) + ), + '
    ' + )), + + ## Model-specific training options + ### Random forest options + conditionalPanel( + "input.showTrainOptions & input.predModel == 'random forest'", + fluidRow( + numericInput("rfNumTrees", label = "Num. trees", + min = 1, max = 2e3, value = 500, step = 1), + numericInput("rfMtry", label = "mtry", + min = 0, max = 1e5, value = 0, step = 1), + numericInput("rfSampleFrac", label = "Sample fraction", + min = 0, max = 1, value = 1, step = .01) + ) + ), + ### Naive Bayes options + conditionalPanel( + "input.showTrainOptions & input.predModel == 'naive bayes'", + flowLayout( + numericInput("naiveSmooth", label = "Smooth", + min = 0, max = 2e3, + value = 1, step = 1e-3), + selectInput("naivePrior", "Prior", + c("uniform", "docfreq", "termfreq")), + selectInput("naiveDistri", "Distribution", + c("multinomial", "Bernoulli")) + ) + ), + ### Lasso options + conditionalPanel( + "input.showTrainOptions & input.predModel == 'lasso'", + strong("Lasso penalty"), + fluidRow( + column( + 6, numericInput("glmLambda", label = NULL, min = 0, max = 2e3, + value = 0, step = 1e-6)), + column( + 6, actionButton("glmCV", label= "Find best (CV)"))) + ), + ### Linear options + conditionalPanel( + "input.showTrainOptions & input.predModel == 'linear'", + strong("Liblinear Cost"), + fluidRow( + column( + 6, numericInput("liblinCost", label= NULL, min= 0, max= 2e10, + value= 32, step= 1)), + column( + 6, actionButton("liblinCV", label= "Find best (CV)"))) + ), + ### KNN options + conditionalPanel( + "input.showTrainOptions & input.predModel == 'knn'", + flowLayout( + strong("N. Neighbours"), + numericInput("knnK", label = NULL, min = 1, max = 1e2, + value = 3, step = 1) + ) + ) + ) + ), + hr() + ), + + + ############################################################################ + ## Main panel set + ############################################################################ + tabsetPanel( + id = "mainPanelset", + selected = "Tagging", + + ######################################################################## + ## Project panel + ######################################################################## + tabPanel( + "Project", + br(), + tabsetPanel( + id = "tabsetProject", + selected = "Sample", + + tabPanel( + "Settings", + br(), + actionButton("saveSystem", "Save changes"), + + h4("Files"), + fluidRow( + column(2, p("Data directory")), + column(4, uiOutput("sys_datadir")), + column(6, p("Place (on the server) where the data and project are stored")) + ), + fluidRow( + column(2, p("Data filename")), + column(4, uiOutput("sys_datafile")), + column(6, p("Main file, containing id and text columns")) + ), + + h4("Variables"), + fluidRow( + column(2, p("ID")), + column(4, uiOutput("sys_var_id")), + column(6, p("Name of the id variable, unique identifier of each text")) + ), + fluidRow( + column(2, p("Text")), + column(4, uiOutput("sys_var_text")), + column(6, p("Name of the text variables: if more than one, texts are concatenated in the specified order")) + ), + fluidRow( + column(2, p("Tags")), + column(4, uiOutput("sys_var_tag")), + column(6, p("Names of scheme variables")) + ), + fluidRow( + column(2, p("Comments")), + column(4, uiOutput("sys_var_comm_ui")), + column(6, p("Name of the comments variable")) + ), + fluidRow( + column(2, p("Context")), + column(4, uiOutput("sys_var_context_ui")), + column(6, p("Names of variables not used in the models, but may be displayed during tagging")) + ), + + h4("System"), + fluidRow( + column(2, checkboxInput("sys_use_python", "Python backend", FALSE)), + column(4, conditionalPanel( + "input.sys_use_python", + textInput("sys_which_python", NULL, value = "python3", + placeholder = "(custom python path)"))), + column(6, conditionalPanel( + "input.sys_use_python", + p("This must be a working python3 environment, with the required modules installed (see documentation)"))) + ), + + conditionalPanel("input.sys_use_python", list( + fluidRow( + column(2, checkboxInput("sys_use_spacy", "SpaCy tokenization", FALSE)), + column(4, conditionalPanel("input.sys_use_spacy", textInput( + "sys_use_spacy_model", NULL, NULL, placeholder = "(spacy model name)"))), + column(6, p("Name of the spacy tokenizer model, used in DTM and word embeddings")) + ), + conditionalPanel("input.sys_use_spacy", fluidRow( + column(2), + column(9, uiOutput("sys_spacyDlUI"))) + ), + fluidRow( + column(2, checkboxInput("sys_use_ft", "FastText word embeddings", FALSE)), + column(4, conditionalPanel("input.sys_use_ft", textInput( + "sys_use_ft_model", NULL, NULL, placeholder = "(fasttext model path)"))), + column(6, p("Path to the local fasttext model binary")) + ), + conditionalPanel("input.sys_use_ft", fluidRow( + column(2), + column(9, uiOutput("sys_ftDlUI"))) + ), + fluidRow( + column(2, checkboxInput("sys_use_sb", "SBERT sentence embeddings", FALSE)), + column(4, conditionalPanel("input.sys_use_sb", textInput( + "sys_use_sb_model", NULL, NULL, + placeholder = "(custom sentence_transformers model)"))), + column(6, p("(GPU recommended) Name or path of the sentence-transformers model")) + ), + + conditionalPanel("input.sys_use_python", list( + checkboxInput("sys_use_gpu", "GPU support (CUDA, for SBERT and BERT)", FALSE), + + br(), + wellPanel( + h4("Model picker"), + fluidRow( + column(2, p("Language")), + column(4, uiOutput("sys_ex_lang_ui")), + column(6, p("Used to preset tokenization and embedding models")) + ), + fluidRow( + column(2), + column(4, strong("Recommended model")), + column(6, strong("Download instructions")) + ), + fluidRow( + column(2, p("SpaCy tokenization")), + column(4, uiOutput("sys_ex_spacy")), + column(6, uiOutput("sys_ex_spacy_dl")) + ), + fluidRow( + column(2, p("FastText word embeddings")), + column(4, uiOutput("sys_ex_ft")), + column(6, uiOutput("sys_ex_ft_dl")) + ), + fluidRow( + column(2, p("SBERT sentence embeddings")), + column(4, uiOutput("sys_ex_sb")), + column(6, p("(Auto download by python module)")) + ) + ) + )) + )) + ), + + tabPanel( + "Sample", + br(), + fluidRow( + column( + 4, + wellPanel( + fluidRow( + column(8, h4("Sample")), + column(4, actionButton("dataImport", "Import", width = "100%"))), + fluidRow( + column(6, numericInput("dataNrows", "N. rows", 500, 10, 1e4, 1)), + column(6, numericInput("dataSkipRows", "Skip rows", 0, 0, step = 1)) + ) + ) + ), + column(8, uiOutput("dataMessage"), uiOutput("panelData")) + ) + ), + + tabPanel( + "Scheme", + br(), + fluidRow( + # column(4, uiOutput("panelScheme")), + column( + 4, + wellPanel( + h4("Current scheme"), + fluidRow( + column(2, HTML(paste0( + "
    ", + actionButton("schemeDelete", "🗑", width = "100%"), + "
    "))), + column(6, uiOutput("printScheme")), + column(4, HTML(paste0( + "
    ", + actionButton("schemeDescrSave", "Save", width = "100%"), + "
    "))) + ), + br(), + textAreaInput("schemeDescr", NULL, width = "100%", rows = 10, + placeholder = "Write scheme description here"), + hr() + ) + ), + column(8, uiOutput("panelRetag")) + ) + ) + ) + ), + + + ######################################################################## + ## Text / visualization panel + ######################################################################## + tabPanel( + "Tagging", + fluidRow( + column( + 3, + br(), + fluidRow( + column(8, textInput("regexFilter", label = NULL, + placeholder = "(Regex filter)")), + column(4, checkboxInput("regexCaseSens", "Case"))), + + wellPanel( + ## Tagging buttons + fluidRow( + column(8, textInput("newLab", label = NULL, + placeholder = "(New label)")), + column(4, actionButton("currentAction", "Create")) + ), + + # fluidRow(uiOutput("oracleButtons")), + uiOutput("oracleButtons"), + + br(), + textInput("currentComment", NULL, "", width = "100%", + placeholder = "(Comment)"), + br(), + uiOutput("makeOracleConfirm") + ), + + # fluidRow( + # column(6, checkboxInput("showContext", "Context")), + # column(6, actionButton("oops", strong("Oops"))) + # ), + checkboxInput("showContext", "Context"), + conditionalPanel("input.showContext", htmlOutput("currentContext")) + ), + column( + 9, + fluidRow( + column(2, checkboxInput("panelText", "Text", TRUE)), + column(2, checkboxInput("panelVisu", "Visualization", FALSE), + offset = 8) + ), + uiOutput("textVisuCols") # Handled in server.R for adaptive columns + ) + ) + ), + + ######################################################################## + ## History panel + ######################################################################## + tabPanel( + "History", + br(), + actionButton("histSave", "Save changes"), + br(), + br(), + DT::dataTableOutput("histDTable") + ), + + + ######################################################################## + ## Stats panel + ######################################################################## + tabPanel( + "Stats", + br(), + fluidRow( + column( + 3, + h3("Counts"), + tableOutput("statsTagTable") + ), + column( + 9, + h3("10-CV diagnostics"), + actionButton("statsCVgo", "Compute 10-CV"), + br(), + verbatimTextOutput("statsCVoutput"), + DT::dataTableOutput("statsCVtable") + ) + ), + hr(), + h3("Gold Standard") + ), + + ######################################################################## + ## BERT panel + ######################################################################## + tabPanel( + "BERT", + + fluidRow( + column( + 3, + br(), + h3("Train new BERT"), + fluidRow( + column(6, actionButton("bertTrain", "Train BERT", width = "100%")), + column(6, checkboxInput("bertOptions", "Options"))), + fluidRow( + column(6, textInput( + "bertSaveName", NULL, placeholder = "(save name)")), + column(6, actionButton("bertSave", "Save", width = "100%"))), + actionLink("bertLast", "Last trained model"), + h3("Saved models"), + uiOutput("bertSaved") + ), + column( + 9, + br(), + conditionalPanel( + "input.bertOptions", + fluidRow( + column(6, selectInput( + "bertModel", "Model", + c("(Fr) CamemBERT-base" = "camembert/camembert-base", + "(Fr) CamemBERT-large" = "camembert/camembert-large", + "(Fr) FlauBERT-small" = "flaubert/flaubert_small_cased", + "(Fr) FlauBERT-base" = "flaubert/flaubert_base_cased", + "(Fr) FlauBERT-large" = "flaubert/flaubert_large_cased", + "(En) DistilBERT-base" = "distilbert-base-cased", + "(En) RoBERTa-base" = "roberta-base", + "(En) DeBERTa-base" = "microsoft/deberta-base", + "(Multi) DistilmBERT-base" = "distilbert-base-multilingual-cased", + "(Multi) MiniLM" = "microsoft/Multilingual-MiniLM-L12-H384", + "(Multi) XLM-RoBERTa-base" = "xlm-roberta-base"))), + column(6) + ), + fluidRow( + column(3, numericInput("bertEpochs", "Epochs", 3, 1, 20, 1)), + column(3, numericInput("bertLrate", "Learning rate", 2e-5, 1e-6, 1, 1e-6)), + column(3, numericInput("bertWdecay", "Weight decay", 0.01, 0, 10, 1e-6)), + column(3) + ), + fluidRow( + column(3, numericInput("bertBatchsize", "Batch size", 4, 1, 32, 1)), + column(3, numericInput("bertGradacc", "Gradient accum.", 4, 1, 32, 1)), + column(3, br(), checkboxInput("bertAdapt", "Adapt token length to batch", TRUE)), + column(3) + ), + fluidRow( + column(3, numericInput("bertValidFrac", "Validation fraction", .2, 0, .9)), + column(3, numericInput("bertValidSeed", "Validation seed", 1234, 1, 9e8)), + column(3, numericInput("bertNeval", "N. validation evals", 10, 1, 100, 1)), + column(3, br(), checkboxInput("bertBest", "Keep best", TRUE)) + ), + fluidRow( + column(3, numericInput("bertMinOccur", "Min. class occurences", 1, 1, 1e4, 1)), + column(3, br(), checkboxInput("bertBalance", "Balance classes", FALSE)), + column(3), + column(3) + ) + ), + + + fluidRow( + column( + 6, + # flowLayout( + # actionButton( + # "bertGoPred", "Infer on current data", width = "100%"), + # actionButton( + # "bertDelete", "Delete saved model", width = "100%")), + verbatimTextOutput("bertMsg")), + column(6, plotOutput("bertValPlot", height = 200))), + + verbatimTextOutput("bertMsgHyperpar"), + + DT::dataTableOutput("bertValstats") + ) + ) + ), + + ######################################################################## + ## Export panel + ######################################################################## + tabPanel( + "Export", + h4("Export tagged data"), + p("Download the tags and predicted probabilities from the complete model, on the current data sample."), + # downloadButton("downloadCsv", "Save csv"), + flowLayout( + selectInput( + "dlTagSelect", NULL, c("tags", "comments", "predictions"), + c("tags", "comments", "predictions"), multiple = TRUE), + selectInput("dlTagFormat", NULL, c("csv", "feather"), "csv"), + downloadButton("dlTagSave", NULL, title = "Save tags") + ), + + hr(), + h4("Export embeddings"), + p("Download the embeddings (incl. from visualization if present), on the current data sample."), + flowLayout( + selectInput( + "dlEmbedSelect", NULL, c("FastText" = "ft", "SBERT" = "sb"), + selected = "sb", multiple = TRUE), + selectInput("dlEmbedFormat", NULL, c("csv", "feather"), "feather"), + downloadButton("dlEmbedSave", NULL, title = "Save embeddings") + ), + + hr(), + h4("Export BERT predictions"), + p("Download the predicted probabilities from the chosen BERT model, on the complete dataset."), + flowLayout( + selectInput("dlBPSelect", NULL, NULL, NULL), + selectInput("dlBPFormat", NULL, c("csv", "feather"), "feather"), + actionButton("dlBPInfer", "Predict"), + verbatimTextOutput("dlBPMsg"), + uiOutput("dlBPDlButton") + ), + + hr(), + h4("Export BERT models") + ) + + ), + br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), br(), + br(), br(), br(), br(), br(), br() +)) diff --git a/docker-images-datalab/activetigger/activetigger/www/active_tigger.jpeg b/docker-images-datalab/activetigger/activetigger/www/active_tigger.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c2e913e5cd6d212c85b26daad10e74236b54348e GIT binary patch literal 106990 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 Y4su0%aO+7*D>+HY-IWL^E>wU2*_8`)4FCWD literal 0 HcmV?d00001 diff --git a/docker-images-datalab/activetigger/activetigger/www/active_tigger.png b/docker-images-datalab/activetigger/activetigger/www/active_tigger.png new file mode 100644 index 0000000000000000000000000000000000000000..587d4ee3758078feed861f8d7e7d726af76fa3bf GIT binary patch literal 250946 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

    W|!|(CFyRxZI|D7}b$uDj5 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 literal 0 HcmV?d00001 diff --git a/docker-images-datalab/activetigger/requirements.r b/docker-images-datalab/activetigger/requirements.r new file mode 100644 index 0000000..c1dc00c --- /dev/null +++ b/docker-images-datalab/activetigger/requirements.r @@ -0,0 +1 @@ +install.packages(c("arrow", "class", "data.table", "DT", "foreign", "glmnet", "haven", "LiblineaR", "Matrix", "Metrics", "quanteda", "quanteda.textmodels", "ranger", "readODS", "readxl", "RJSONIO", "rlang", "Rtsne", "shiny", "SparseM", "stringi", "uwot")) diff --git a/docker-images-datalab/activetigger/requirementspython.txt b/docker-images-datalab/activetigger/requirementspython.txt new file mode 100644 index 0000000..6bbb052 --- /dev/null +++ b/docker-images-datalab/activetigger/requirementspython.txt @@ -0,0 +1,6 @@ +pip install argparse datasets fasttext numpy pandas pyarrow sklearn +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 +pip install transformers[torch] +pip install sentence_transformers +pip install -U typing-inspect==0.8.0 typing_extensions==4.6.1 +pip install spacy \ No newline at end of file diff --git a/docker-images-datalab/index.md b/docker-images-datalab/index.md new file mode 100644 index 0000000..8f9b045 --- /dev/null +++ b/docker-images-datalab/index.md @@ -0,0 +1,3 @@ +## Helm charts datalab du Genes + +Dépôts regroupant l'ensemble des images dockers créer par le genes et qui seront utilisées pour le déploiement de nouveau services dans le catalogue datalab via helm chart \ No newline at end of file diff --git a/docker-images-datalab/requirementspython.txt b/docker-images-datalab/requirementspython.txt new file mode 100644 index 0000000..af56f7c --- /dev/null +++ b/docker-images-datalab/requirementspython.txt @@ -0,0 +1,15 @@ +argparse +datasets +fasttext +numpy +pandas +pyarrow +scikit-learn +torch==1.8.0+cu118 +torchvision==0.9.0+cu118 +torchaudio==0.8.0 +transformers +sentence_transformers +typing-inspect==0.8.0 +typing-extensions==4.6.1 +spacy \ No newline at end of file diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/.helmignore b/helm-charts-datalab/helm-charts-test/charts/activetigger/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/Chart.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/Chart.yaml new file mode 100644 index 0000000..f71d675 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: activetigger +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.0.0" diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/index.md b/helm-charts-datalab/helm-charts-test/charts/activetigger/index.md new file mode 100644 index 0000000..f4d05fe --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/index.md @@ -0,0 +1,63 @@ +# dépôt pour déploiement automatique d'un serveur shiny (R + python) pour mise à disposition à des étudiants pour quelques mois + +A voir la configuration de l'ingress dans le fichier values.yaml du helm chart activetigger + +Image docker où les consignes de déploiement ont été ajoutées : https://code.groupe-genes.fr/DSIT/datalab/src/branch/main/docker-images-datalab/activetigger + +# CONSIGNE DE DEPLOIEMENT + +Voici le git d'Active Tigger : https://gitlab.univ-lille.fr/julien.boelaert/activetigger +Le programme nécessite python, R et shiny server. + +## Packages R (à installer depuis l'exécutable R employé par shiny server) +``` +install.packages(c("arrow", "class", "data.table", "DT", "foreign", "glmnet", "haven", "LiblineaR", "Matrix", "Metrics", "quanteda", "quanteda.textmodels", "ranger", "readODS", "readxl", "RJSONIO", "rlang", "Rtsne", "shiny", "SparseM", "stringi", "uwot")) +``` + +## Environnement python +``` +conda create -n tigger python==3.10 +conda activate tigger +``` + +``` +pip install argparse datasets fasttext numpy pandas pyarrow sklearn +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118 +pip install transformers[torch] +pip install sentence_transformers +pip install -U typing-inspect==0.8.0 typing_extensions==4.6.1 +pip install spacy +``` +## Téléchargement des modèles spacy et fasttext + +### Français +``` +python -m spacy download fr_core_news_sm +cd ~ +wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.fr.300.bin.gz +gunzip cc.fr.300.bin.gz +``` +### Anglais +``` +python -m spacy download en_core_web_sm +cd ~ +wget https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.en.300.bin.gz +gunzip cc.en.300.bin.gz +``` + +# A chaque création d'instance + +## Clone git pour créer la nouvelle instance (remplacer "tigger-name" par le nom que prendra l'instance, ie https://analytics.huma-num.fr/Prenom.Nom/tigger-name/) +``` +cd ~/zPublish/shiny +git clone https://gitlab.univ-lille.fr/julien.boelaert/activetigger.git tigger-name +``` +## Dans l'application + +Tout en haut à gauche, bouton "+" pour "create project". Puis dans les champs : + +- data directory: moi j'utilise toujours ~/tagging/domaine (genre ~/tagging/radio ou ~/tagging/journaux), mais c'est à toi de voir où tu veux que les données et tags soient stockées sur ton serveur +- je conseille de cocher toutes les cases : python, spacy, fasttext, sbert, gpu +- python : "~/conda/envs/tigger/bin/python" +- fasttext : "~/cc.fr.300.bin" (càd qu'il faut donner le chemin du modèle sur ton serveur, pas juste le nom) +- spacy et SBERT : garder les valeurs par défaut pour la langue choisie \ No newline at end of file diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/NOTES.txt b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/NOTES.txt new file mode 100644 index 0000000..52c6bee --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test1.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test1.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test1.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test1.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/_helpers.tpl b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/_helpers.tpl new file mode 100644 index 0000000..3625df9 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "test1.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "test1.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "test1.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "test1.labels" -}} +helm.sh/chart: {{ include "test1.chart" . }} +{{ include "test1.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "test1.selectorLabels" -}} +app.kubernetes.io/name: {{ include "test1.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "test1.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "test1.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/deployment.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/deployment.yaml new file mode 100644 index 0000000..bd09077 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "test1.fullname" . }} + labels: + {{- include "test1.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "test1.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "test1.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "test1.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/hpa.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/hpa.yaml new file mode 100644 index 0000000..f0b4cfe --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "test1.fullname" . }} + labels: + {{- include "test1.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "test1.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/ingress.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/ingress.yaml new file mode 100644 index 0000000..8a26ce5 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "test1.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "test1.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/service.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/service.yaml new file mode 100644 index 0000000..34760fd --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "test1.fullname" . }} + labels: + {{- include "test1.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "test1.selectorLabels" . | nindent 4 }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/serviceaccount.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/serviceaccount.yaml new file mode 100644 index 0000000..5e8f157 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "test1.serviceAccountName" . }} + labels: + {{- include "test1.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/tests/test-connection.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/tests/test-connection.yaml new file mode 100644 index 0000000..cf40217 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "test1.fullname" . }}-test-connection" + labels: + {{- include "test1.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "test1.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm-charts-datalab/helm-charts-test/charts/activetigger/values.yaml b/helm-charts-datalab/helm-charts-test/charts/activetigger/values.yaml new file mode 100644 index 0000000..ab2c50c --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/activetigger/values.yaml @@ -0,0 +1,107 @@ +# Default values for test1. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: https://code.groupe-genes.fr/dsit/datalab/src/branch/main/docker-images-datalab/activetigger + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "latest" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 8000 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/.helmignore b/helm-charts-datalab/helm-charts-test/charts/overleaf/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/Chart.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/Chart.yaml new file mode 100644 index 0000000..ac43f14 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: overleaf +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/index.md b/helm-charts-datalab/helm-charts-test/charts/overleaf/index.md new file mode 100644 index 0000000..f6146c0 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/index.md @@ -0,0 +1,20 @@ +## Informations utiles + +Exemple insee: +chart: + https://github.com/InseeFrLab/helm-charts-interactive-services/blob/main/charts/vscode-python/Chart.yaml + +dockerfile: + https://github.com/InseeFrLab/images-datascience/blob/main/vscode/Dockerfile + + +OVERLEAF: +image: +https://github.com/overleaf/overleaf + https://github.com/overleaf/overleaf/blob/main/server-ce/Dockerfile + https://github.com/overleaf/toolkit/ + https://github.com/overleaf/toolkit/blob/master/doc/quick-start-guide.md + +helm chart exemple: +https://artifacthub.io/packages/helm/geek-cookbook/overleaf +https://github.com/geek-cookbook/charts/tree/main/charts/stable/overleaf \ No newline at end of file diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/NOTES.txt b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/NOTES.txt new file mode 100644 index 0000000..c8680d6 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "overleaf.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "overleaf.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "overleaf.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "overleaf.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/_helpers.tpl b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/_helpers.tpl new file mode 100644 index 0000000..8fe42d4 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "overleaf.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "overleaf.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "overleaf.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "overleaf.labels" -}} +helm.sh/chart: {{ include "overleaf.chart" . }} +{{ include "overleaf.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "overleaf.selectorLabels" -}} +app.kubernetes.io/name: {{ include "overleaf.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "overleaf.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "overleaf.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/deployment.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/deployment.yaml new file mode 100644 index 0000000..e6e8a6b --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "overleaf.fullname" . }} + labels: + {{- include "overleaf.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "overleaf.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "overleaf.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "overleaf.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/hpa.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/hpa.yaml new file mode 100644 index 0000000..a9c4988 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "overleaf.fullname" . }} + labels: + {{- include "overleaf.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "overleaf.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/ingress.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/ingress.yaml new file mode 100644 index 0000000..8e87c01 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "overleaf.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "overleaf.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/service.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/service.yaml new file mode 100644 index 0000000..407f9b3 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "overleaf.fullname" . }} + labels: + {{- include "overleaf.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "overleaf.selectorLabels" . | nindent 4 }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/serviceaccount.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/serviceaccount.yaml new file mode 100644 index 0000000..78193cb --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "overleaf.serviceAccountName" . }} + labels: + {{- include "overleaf.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/tests/test-connection.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/tests/test-connection.yaml new file mode 100644 index 0000000..d00c184 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "overleaf.fullname" . }}-test-connection" + labels: + {{- include "overleaf.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "overleaf.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm-charts-datalab/helm-charts-test/charts/overleaf/values.yaml b/helm-charts-datalab/helm-charts-test/charts/overleaf/values.yaml new file mode 100644 index 0000000..4ce91cd --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/overleaf/values.yaml @@ -0,0 +1,107 @@ +# Default values for overleaf. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: sharelatex/sharelatex + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: 4 + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/.helmignore b/helm-charts-datalab/helm-charts-test/charts/test2/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/Chart.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/Chart.yaml new file mode 100644 index 0000000..df7f09c --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: test2 +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/NOTES.txt b/helm-charts-datalab/helm-charts-test/charts/test2/templates/NOTES.txt new file mode 100644 index 0000000..8f6d373 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test2.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test2.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test2.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test2.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/_helpers.tpl b/helm-charts-datalab/helm-charts-test/charts/test2/templates/_helpers.tpl new file mode 100644 index 0000000..b34baed --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "test2.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "test2.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "test2.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "test2.labels" -}} +helm.sh/chart: {{ include "test2.chart" . }} +{{ include "test2.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "test2.selectorLabels" -}} +app.kubernetes.io/name: {{ include "test2.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "test2.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "test2.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/deployment.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/deployment.yaml new file mode 100644 index 0000000..f8ef1db --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "test2.fullname" . }} + labels: + {{- include "test2.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "test2.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "test2.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "test2.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/hpa.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/hpa.yaml new file mode 100644 index 0000000..25f8047 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "test2.fullname" . }} + labels: + {{- include "test2.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "test2.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/ingress.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/ingress.yaml new file mode 100644 index 0000000..3a4bbf6 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "test2.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "test2.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/service.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/service.yaml new file mode 100644 index 0000000..77975d4 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "test2.fullname" . }} + labels: + {{- include "test2.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "test2.selectorLabels" . | nindent 4 }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/serviceaccount.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/serviceaccount.yaml new file mode 100644 index 0000000..d7e0a53 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "test2.serviceAccountName" . }} + labels: + {{- include "test2.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/templates/tests/test-connection.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/templates/tests/test-connection.yaml new file mode 100644 index 0000000..e09a13e --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "test2.fullname" . }}-test-connection" + labels: + {{- include "test2.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "test2.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm-charts-datalab/helm-charts-test/charts/test2/values.yaml b/helm-charts-datalab/helm-charts-test/charts/test2/values.yaml new file mode 100644 index 0000000..5a142e5 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test2/values.yaml @@ -0,0 +1,107 @@ +# Default values for test2. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/.helmignore b/helm-charts-datalab/helm-charts-test/charts/test3/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/Chart.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/Chart.yaml new file mode 100644 index 0000000..7c447b0 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: test3 +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/NOTES.txt b/helm-charts-datalab/helm-charts-test/charts/test3/templates/NOTES.txt new file mode 100644 index 0000000..ca312c2 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "test3.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "test3.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "test3.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "test3.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/_helpers.tpl b/helm-charts-datalab/helm-charts-test/charts/test3/templates/_helpers.tpl new file mode 100644 index 0000000..a9b6c9e --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "test3.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "test3.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "test3.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "test3.labels" -}} +helm.sh/chart: {{ include "test3.chart" . }} +{{ include "test3.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "test3.selectorLabels" -}} +app.kubernetes.io/name: {{ include "test3.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "test3.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "test3.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/deployment.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/deployment.yaml new file mode 100644 index 0000000..01a1afd --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "test3.fullname" . }} + labels: + {{- include "test3.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "test3.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "test3.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "test3.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/hpa.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/hpa.yaml new file mode 100644 index 0000000..8282d6c --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "test3.fullname" . }} + labels: + {{- include "test3.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "test3.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/ingress.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/ingress.yaml new file mode 100644 index 0000000..e813ac4 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "test3.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "test3.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/service.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/service.yaml new file mode 100644 index 0000000..6d7ecf8 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "test3.fullname" . }} + labels: + {{- include "test3.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "test3.selectorLabels" . | nindent 4 }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/serviceaccount.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/serviceaccount.yaml new file mode 100644 index 0000000..b6bae8a --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "test3.serviceAccountName" . }} + labels: + {{- include "test3.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/templates/tests/test-connection.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/templates/tests/test-connection.yaml new file mode 100644 index 0000000..2b47022 --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "test3.fullname" . }}-test-connection" + labels: + {{- include "test3.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "test3.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/helm-charts-datalab/helm-charts-test/charts/test3/values.yaml b/helm-charts-datalab/helm-charts-test/charts/test3/values.yaml new file mode 100644 index 0000000..e2551bd --- /dev/null +++ b/helm-charts-datalab/helm-charts-test/charts/test3/values.yaml @@ -0,0 +1,107 @@ +# Default values for test3. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/helm-charts-datalab/index.md b/helm-charts-datalab/index.md new file mode 100644 index 0000000..796a044 --- /dev/null +++ b/helm-charts-datalab/index.md @@ -0,0 +1,3 @@ +## Helm charts test + +dépôt de test pour ajouter de nouveau service au catalogue datalab \ No newline at end of file diff --git a/index.md b/index.md new file mode 100644 index 0000000..6c6d2a2 --- /dev/null +++ b/index.md @@ -0,0 +1,5 @@ +## Helm charts datalab du Genes + +Dépôts regroupant l'ensemble des helm-charts utilisé pour le déploiement des services, cette version est actuellement en test + +Voir le fichier "values.yaml" pour rajouter un nouveau dépôt au catalogue du datalab \ No newline at end of file diff --git a/kk b/kk deleted file mode 100644 index a427aba..0000000 --- a/kk +++ /dev/null @@ -1 +0,0 @@ -hh \ No newline at end of file diff --git a/values.yaml b/values.yaml new file mode 100644 index 0000000..59e17dc --- /dev/null +++ b/values.yaml @@ -0,0 +1,18 @@ +## texte à rajouter sur le fichier values.yaml de l'app onyxia pour ajouter le catalogue test (https://code.groupe-genes.fr/DSIT/kube-apps/src/branch/main/apps/onyxia/values.yaml) + + { + "id": "test", + "name": "test", + "description": "test services.", + "maintainer": "support@groupe-genes.fr", + "location": "link vers dépots à rajouter une fois créé", + "status": "PROD", + "highlightedCharts": + [ + "overleaf", + "activetigger", + "test2", + "test3", + ], + "type": "helm", + }, \ No newline at end of file