From 93bb33dfb4bc453a8877269f0ee5a93f1d6433c0 Mon Sep 17 00:00:00 2001
From: "steven.liatti" <steven@liatti.ch>
Date: Thu, 15 Oct 2020 15:53:59 +0200
Subject: [PATCH] Update doc, remove old unnecessary scripts

---
 README.md                           | 125 +++++++++++++++++++++-------
 create_group_and_repos.sh           |  22 -----
 doc/project_id.png                  | Bin 8378 -> 0 bytes
 project_id.png                      | Bin 0 -> 10873 bytes
 pwm                                 |   2 +-
 scripts/clone_all.py                |  77 -----------------
 scripts/create_group.py             |  32 -------
 scripts/create_repo_for_students.py |  77 -----------------
 8 files changed, 95 insertions(+), 240 deletions(-)
 delete mode 100755 create_group_and_repos.sh
 delete mode 100644 doc/project_id.png
 create mode 100644 project_id.png
 delete mode 100755 scripts/clone_all.py
 delete mode 100755 scripts/create_group.py
 delete mode 100755 scripts/create_repo_for_students.py

diff --git a/README.md b/README.md
index a8d7e91..1430045 100644
--- a/README.md
+++ b/README.md
@@ -1,63 +1,126 @@
 # Practical Work Manager (pwm)
 
-Ce repository contient différents scripts python pour gérer la réalisation de travaux pratiques par les étudiants avec la contrainte d'utiliser le gitlab d'HEPIA.
+Programme python pour gérer les travaux pratiques des étudiants avec la contrainte d'utiliser le gitlab d'HEPIA.
+
+## TL;DR
+
+- Créer un groupe et les repositories en une seule commande (avec un repository "image" optionnel) : `./pwm -t TOKEN group_repos GROUP_NAME REPOS_FILE [-i IMPORT_URL]`, voir [Syntaxe du fichier YAML (REPOS_FILE)](#syntaxe-du-fichier-yaml-repos_file)
+- Clone tous les projets des étudiants d'un groupe gitlab (`ID`) dans un répertoire créé à la volée : `./pwm -t TOKEN clone ID DIRECTORY`
+
+- [Practical Work Manager (pwm)](#practical-work-manager-pwm)
+  - [TL;DR](#tldr)
+  - [Workflow d'utilisation](#workflow-dutilisation)
+  - [Utilisation de pwm](#utilisation-de-pwm)
+    - [Création d'un groupe et des projets](#création-dun-groupe-et-des-projets)
+    - [Création d'un groupe seulement](#création-dun-groupe-seulement)
+    - [Création d'un sous-projet dans le groupe](#création-dun-sous-projet-dans-le-groupe)
+    - [Clone de tous les repositories](#clone-de-tous-les-repositories)
+  - [Syntaxe du fichier YAML (REPOS_FILE)](#syntaxe-du-fichier-yaml-repos_file)
+    - [Noms et emails](#noms-et-emails)
+    - [Emails seulement](#emails-seulement)
 
 ## Workflow d'utilisation
 Pour expliquer la démarche d'utilisation, prenons le scénario suivant :
-- L'enseignant Michel Albuquerque prépare un nouveau travail pratique (TP). Il souhaite forcer les étudiants à utiliser git et githepia pour qu'ils versionnent leur code et pour qu'il puisse visualiser et recevoir leur rendus.
+- L'enseignant Michel Albuquerque prépare un nouveau travail pratique (TP). Il souhaite forcer les étudiants à utiliser git et gitedu pour qu'ils versionnent leur code et pour qu'il puisse visualiser et recevoir leur rendus.
 - Les TPs sont à faire par groupe ou de manière individuelle.
 - Pour transmettre son énoncé et des fichiers (exemples, squelette de code, librairies, binaires, etc.) aux étudiants, Michel Albuquerque crée un repository git accessible publiquement, nommé "super-tp".
 - Grâce à ce repository "super-tp", il peut mettre à jour le contenu distribué aux étudiants, en leur offrant la possibilité de visualiser les changements incrémentaux survenus.
 
-Le moment est venu de créer les dépôts git pour chaque groupe/étudiant suivant le cours et devant réaliser le TP. Sur la base d'une liste de groupes ou d'étudiants, Michel Albuquerque pourra utiliser les scripts suivants pour :
+Le moment est venu de créer les dépôts git pour chaque groupe/étudiant suivant le cours et devant réaliser le TP. Sur la base d'une liste de groupes ou d'étudiants, Michel Albuquerque pourra utiliser le programme pour :
 1. Créer le groupe (namespace) dédié au cours/TP, contenant tous les repositories des étudiants.
 1. Créer chaque repository pour chaque groupe/étudiant avec les contraintes nécessaires (privé, accessible à (aux) l'étudiant(s) concerné(s), aux enseignants, etc.)
 1. Récupérer (clone) sur sa machine tous les repositories d'un seul coup, dans des répertoires séparés, au moment du rendu par exemple.
 
-La section suivante décrit l'utilisation des scripts.
+## Utilisation de pwm
 
-## Utilisation des scripts
+Ce programme est écrit en python et testé avec la version 3.9.0, avec les dépendances suivantes (voir `requirements.txt`) :
 
-Tous les scripts sont (actuellement) écrits en python et testés avec la version 3.6.8, sans dépendances à des librairies externes. Ils se présentent sous la forme de programmes à lancer dans un shell en ligne de commande. Ils nécessitent tous un `token` gitlab, pouvant être généré [sur cette page](https://gitedu.hesge.ch/profile/personal_access_tokens), en cochant la case "api". Certains attendent également un `project_id` correspond à celui affiché sur la page de repo :
+```
+requests
+pyyaml
+```
 
-![image](doc/project_id.png)
+Pour rappel, pour ne pas à avoir à installer ces deux dépendances au niveau système, les commandes suivantes génèrent un environnement virtuel :
 
-### create_group.py
 ```bash
-python3 create_group.py <token> <group_name> <visibility>
+python -m venv .venv
+source .venv/bin/activate
+pip install -r requirements.txt
 ```
-Crée un groupe au sens gitlab du terme, nommé `group_name`, avec la visibilité optionnelle `visibility` (`private`, `internal` ou `public`), par défaut privée. Si le groupe existe déjà, ne fait rien. Retourne le `group_id` du groupe créé, nécessaire pour `create_repo_for_students.py` par exemple.
 
-### create_repo_for_students.py
+pwm se présente sous la forme d'une CLI. Il nécessite un `token` gitlab, pouvant être généré [sur cette page](https://gitedu.hesge.ch/profile/personal_access_tokens), en cochant la case "api". Ce `token` peut ensuite être utilisé de trois manières :
+
+1. Écrit dans le fichier `~/.gitedu_token`
+2. Placé dans la variable d'environnement `GITEDU_TOKEN`
+3. Donné en argument de `pwm` avec l'option `-t` ou `--token`
+
+Selon les commandes, un `project_id` ou `group_id` est également nécessaire, il correspond à celui affiché sur la page du groupe / projet :
+
+![image](project_id.png)
+
+L'exécution du programme sans arguments affiche l'aide et le détail pour chaque sous-commande.
+
+### Création d'un groupe et des projets
 ```bash
-python3 create_repo_for_students.py <token> <import_url> <group_id> <project_name> <student-mail1,student-mail2,...,student-mailN> <expires_at>
+python pwm group_repos GROUP_NAME REPOS_FILE [-h] [--visibility VISIBILITY] [-i IMPORT_URL] [-x EXPIRES_AT]
 ```
-Crée un dépôt git (projet) au sein d'un groupe à partir de l'URL d'un projet existant pour une liste d'emails d'étudiants. Détail des arguments :
-- `token` : le token gitlab.
-- `import_url` : l'URL (http) du projet (repository) existant. Ce projet doit être public.
-- `group_id` : l'identifiant du groupe dédié au cours/TP, créé précédemment (avec `create_group.py` par exemple).
-- `project_name` : le nom du nouveau repository à créer pour le ou les étudiants concernés.
-- `student-mail1,student-mail2,...,student-mailN` : une liste d'emails des étudiants. Les emails sont séparés par une virgule. Peut contenir un seul email.
-- `expires_at`: optionnel, au format `AAAA-MM-DD`, supprime les étudiants ajoutés à la date donnée.
-
-### clone_all_repos_in_group.py
+Exécute les opérations de création de groupe et de repositories à partir d'un fichier YAML (voir [Syntaxe du fichier YAML (REPOS_FILE)](#syntaxe-du-fichier-yaml-repos_file)). Voir les sous-sections suivantes pour les détails des sous-commandes.
+
+### Création d'un groupe seulement
 ```bash
-python3 clone_all_repos_in_group.py <token> <group_id> <directory> <until_date>
+python pwm group [-h] [--visibility VISIBILITY] GROUP_NAME
 ```
-Clone tous les repositories d'un groupe `group_id` donné dans un répertoire nommé `directory`. Si une date `until_date` (au format `AAAA-MM-DD hh:mm`) est donnée, exécute un `git checkout` sur le premier commit précédant cette date. Affiche sur la sortie standard les membres du groupe, l'url web du repo et dans quel sous-répertoire se trouvent les fichiers.
+Crée un groupe au sens gitlab du terme, nommé `GROUP_NAME`, avec la visibilité optionnelle `VISIBILITY` (`private`, `internal` ou `public`), par défaut privée. Si le groupe existe déjà, ne fait rien. Retourne le `group_id` du groupe créé, nécessaire pour la création des sous-projets par exemple.
 
-### clone_all_forks.py
+### Création d'un sous-projet dans le groupe
 ```bash
-python3 clone_all_forks.py <token> <project_id> <directory> <until_date>
+python pwm repo [-h] [-n NAME] [-i IMPORT_URL] [-x EXPIRES_AT] GROUP_ID EMAILS
 ```
-Clone tous les forks d'un projet `project_id` donné dans un répertoire nommé `directory`. Si une date `until_date` (au format `AAAA-MM-DD hh:mm`) est donnée, exécute un `git checkout` sur le premier commit précédant cette date. Affiche sur la sortie standard les membres du groupe (avec un droit d'accès supérieur à *Reporter*), l'url web du repo et dans quel sous-répertoire se trouvent les fichiers.
+Crée un dépôt git (projet) au sein d'un groupe à partir de l'URL d'un projet existant pour une liste d'emails d'étudiants. Détail des arguments :
+- `NAME` : optionnel, le nom du nouveau repository à créer pour le ou les étudiants concernés. Si non renseigné, prend la première partie du premier email dans `EMAILS`.
+- `IMPORT_URL` : optionnel, l'URL (http) du projet (repository) existant. Ce projet doit être public.
+- `EXPIRES_AT`: optionnel, au format `YYYY-MM-DD`, supprime les étudiants ajoutés à la date donnée (ils ne peuvent plus `push`).
+- `GROUP_ID` : l'identifiant du groupe dédié au cours/TP, créé précédemment.
+- `EMAILS` : une liste d'emails des étudiants. Les emails sont séparés par une virgule. Peut contenir un seul email.
 
-## "Convenient" script
+### Clone de tous les repositories
 ```bash
-./create_group_and_repos.sh <token> <group_name> <import_url> <repos_students>
+python pwm clone [-h] [-g | -f] [-u UNTIL_DATE] ID DIRECTORY
 ```
-Un script bash est également disponible, `create_group_and_repos.sh` qui permet de "batcher" les opérations de création de groupe et de repositories à partir d'un fichier texte `repos_students` formaté ainsi :
+Clone tous les repositories d'un groupe (`-g`) ou tous les forks d'un projet (`-f`) selon l'id (`ID`) donné dans un répertoire nommé `DIRECTORY`. Si une date `UNTIL_DATE` (au format `YYYY-MM-DD hh:mm`) est donnée, exécute un `git checkout` sur le premier commit précédant cette date. Affiche sur la sortie standard les membres du groupe (avec un droit d'accès supérieur à *Reporter*), l'url web du repo et dans quel sous-répertoire se trouvent les fichiers.
+
+## Syntaxe du fichier YAML (REPOS_FILE)
+Le fichier YAML doit respecter une des deux syntaxes suivantes.
+
+### Noms et emails
+Pour chaque projet créé, un nom et une liste d'emails doivent être renseignés :
+
+```yaml
+- name: group1
+  emails:
+  - prenom.nom11@hesge.ch
+  - prenom.nom12@hesge.ch
+- name: group2
+  emails:
+  - prenom.nom21@hesge.ch
+  - prenom.nom22@hesge.ch
+- name: group3
+  emails:
+  - prenom.nom31@hesge.ch
+  - prenom.nom32@hesge.ch
 ```
-repository1;email1,email2
-repository2;email3,email4
+
+### Emails seulement
+Si uniquement les emails sont fournis, prend le premier nom de chaque email pour nom de projet :
+
+```yaml
+- emails:
+  - prenom.nom11@hesge.ch # project_name = prenom.nom11
+  - prenom.nom12@hesge.ch
+- emails:
+  - prenom.nom21@hesge.ch # project_name = prenom.nom21
+  - prenom.nom22@hesge.ch
+- emails:
+  - prenom.nom31@hesge.ch # project_name = prenom.nom31
+  - prenom.nom32@hesge.ch
 ```
diff --git a/create_group_and_repos.sh b/create_group_and_repos.sh
deleted file mode 100755
index 15bc4bb..0000000
--- a/create_group_and_repos.sh
+++ /dev/null
@@ -1,22 +0,0 @@
-#!/usr/bin/env bash
-
-if [[ $# != 4 ]]; then
-    echo "Usage: $0 <token> <group_name> <import_url> <repos_students>"
-    exit 1
-fi
-
-token=$1
-group_name=$2
-import_url=$3
-repos_students=$4
-
-group=$(scripts/create_group.py $token $group_name)
-group_id=$(echo $group | cut -d';' -f2)
-printf "$group\n\n"
-
-for line in $(cat $repos_students); do
-    project_name=$(echo $line | cut -d';' -f1)
-    students=$(echo $line | cut -d';' -f2)
-    new_repo=$(scripts/create_repo_for_students.py $token $import_url $group_id "$project_name" $students)
-    printf "$new_repo\n\n"
-done
diff --git a/doc/project_id.png b/doc/project_id.png
deleted file mode 100644
index 2671a3fb443acc0b326d14adaf81f5473fb2ea0a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 8378
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
Gp$Py{I+n-)

diff --git a/project_id.png b/project_id.png
new file mode 100644
index 0000000000000000000000000000000000000000..37a6aa7b7f25ebf52924164b71f87dacbe004c7f
GIT binary patch
literal 10873
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

literal 0
HcmV?d00001

diff --git a/pwm b/pwm
index ec1aaf8..51b0a32 100755
--- a/pwm
+++ b/pwm
@@ -248,7 +248,7 @@ if __name__ == '__main__':
     parser_group_repos.add_argument(
         "group_name", metavar="GROUP_NAME", help="The group name.")
     parser_group_repos.add_argument(
-        "repos_file", metavar="REPOS_FILE", help="A file with projects names and students emails.")
+        "repos_file", metavar="REPOS_FILE", help="YAML file with projects names and/or students emails.")
     parser_group_repos.add_argument(
         "--visibility", help="Group visibility. By default private.")
     parser_group_repos.add_argument("-i", "--import_url",
diff --git a/scripts/clone_all.py b/scripts/clone_all.py
deleted file mode 100755
index f02c74a..0000000
--- a/scripts/clone_all.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import requests
-import subprocess
-import argparse
-
-parser = argparse.ArgumentParser()
-group = parser.add_mutually_exclusive_group()
-group.add_argument("-g", "--group", action="store_true", help="Clone repositories from a group (with group_id) or forks of a project (with project_id) (default behavior).")
-group.add_argument("-f", "--forks", action="store_true", help="Clone forks of a project (with project_id).")
-parser.add_argument(
-    "token", metavar="TOKEN", help="Create a token here: https://gitedu.hesge.ch/profile/personal_access_tokens")
-parser.add_argument(
-    "id", metavar="ID", help="The group_id (int) of the projects or the project_id (int) of the forks.")
-parser.add_argument(
-    "directory", metavar="DIRECTORY", help="Local directory where clone all repositories.")
-parser.add_argument(
-    "-u", "--until_date", help="Do a git checkout for all repositories at given date, format \"YYYY-MM-DD hh:mm\" (optional).")
-args = parser.parse_args()
-
-try:
-    os.mkdir(args.directory)
-except OSError:
-    print("Creation of the directory '%s' failed, exit\n" % args.directory)
-    exit(1)
-
-base_url = 'https://gitedu.hesge.ch/api/v4/'
-params = {'simple': 'true', 'per_page': 100}
-headers = {'PRIVATE-TOKEN': args.token}
-
-if args.forks:
-    url = base_url + 'projects/' + args.id + '/forks'
-else:
-    url = base_url + 'groups/' + args.id + '/projects'
-
-repositories = requests.get(url, params=params, headers=headers).json()
-if 'message' in repositories:
-    print('Error retrieving repositories: ' + repositories['message'])
-    exit(1)
-
-for repo in repositories:
-    repo_url = base_url + '/projects/' + str(repo['id']) + '/members'
-    members = requests.get(repo_url, headers=headers).json()
-    if 'message' in members:
-        print('Error retrieving members: ' + members['message'])
-        exit(1)
-
-    ssh_url_to_repo = repo['ssh_url_to_repo']
-    web_url = repo['web_url']
-    members_names = ''
-
-    for member in members:
-        if member['access_level'] > 20:  # Access level greater than "Reporter"
-            members_names += member['username'] + ', '
-
-    if args.forks:
-        repo_local_name = repo['namespace']['path']
-    else:
-        repo_local_name = repo['path']
-
-    print('Members: ' + members_names)
-    print('Web url: ' + web_url)
-    print('Cloning in "' + args.directory + '/' + repo_local_name + '"')
-
-    subprocess.run(["git", "clone", "-q", ssh_url_to_repo,
-                    args.directory + '/' + repo_local_name])
-    if args.until_date:
-        commit_id = subprocess.check_output([
-            "git", "rev-list", "-n", "1", "--before=\"" + args.until_date + "\"",
-            "master"], cwd=args.directory + '/' + repo_local_name).decode('utf-8').rstrip()
-        subprocess.run(
-            ["git", "checkout", "-q", str(commit_id)],
-            cwd=args.directory + '/' + repo_local_name)
-        print("Checkout at " + str(commit_id) + "\n")
-    else:
-        print()
diff --git a/scripts/create_group.py b/scripts/create_group.py
deleted file mode 100755
index 209e4c3..0000000
--- a/scripts/create_group.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import requests
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    "token", metavar="TOKEN", help="Create a token here: https://gitedu.hesge.ch/profile/personal_access_tokens")
-parser.add_argument(
-    "group_name", metavar="GROUP_NAME", help="The group name.")
-parser.add_argument(
-    "--visibility", help="Group visibility. By default private.")
-args = parser.parse_args()
-
-if args.visibility:
-    visibility = args.visibility
-else:
-    visibility = 'private'
-
-base_url = 'https://gitedu.hesge.ch/api/v4/'
-params = {'path': args.group_name,
-          'name': args.group_name, 'visibility': visibility}
-headers = {'PRIVATE-TOKEN': args.token}
-
-group = requests.post(base_url + '/groups',
-                      params=params, headers=headers).json()
-if 'message' in group:
-    print('Error in creating group: %s' % group)
-    exit(1)
-
-print("Group '" + group['name'] + "' with id '" + str(group['id']) + "' and visibility '" +
-      group['visibility'] + "' available at '" + group['web_url'] + "' ;" + str(group['id']))
diff --git a/scripts/create_repo_for_students.py b/scripts/create_repo_for_students.py
deleted file mode 100755
index 89480aa..0000000
--- a/scripts/create_repo_for_students.py
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import requests
-
-parser = argparse.ArgumentParser()
-parser.add_argument(
-    "token", metavar="TOKEN", help="Create a token here: https://gitedu.hesge.ch/profile/personal_access_tokens")
-parser.add_argument(
-    "group_id", metavar="GROUP_ID", help="The group id (int) where to store the created new project.")
-parser.add_argument(
-    "emails", metavar="EMAILS", help="Emails list of students working in this project, separated by commas (email1,email2).")
-parser.add_argument(
-    "-n", "--name", help="The project name. If blank, take the first student name (from email) as name.")
-parser.add_argument("-i", "--import_url",
-                    help="Import the publicly accessible project by URL given here (optional).")
-parser.add_argument("-x", "--expires_at",
-                    help="Expiration date to kick off students from this project, at 00:00:00. YYYY-MM-DD format (optional).")
-args = parser.parse_args()
-
-base_url = 'https://gitedu.hesge.ch/api/v4'
-headers = {'PRIVATE-TOKEN': args.token}
-
-# split '@' in the case when project name = student's email
-if args.name:
-    name = args.name
-else:
-    name = args.emails.split('@')[0]
-
-# Get students ids from their emails
-users_emails = args.emails.split(',')
-user_ids = []
-for email in users_emails:
-    user_requested = requests.get(
-        base_url + '/users', params={'search': email}, headers=headers).json()
-    if len(user_requested) == 0:
-        print('No user %s found, operation aborted' % email)
-        exit(1)
-    user_id = user_requested[0]['id']
-    user_ids.append(user_id)
-
-# Create project from name, import_url (if given) and group_id
-params = {'name': name, 'namespace_id': args.group_id, 'visibility': 'private'}
-if args.import_url:
-    params['import_url'] = args.import_url
-project = requests.post(base_url + '/projects',
-                        params=params, headers=headers).json()
-if 'message' in project:
-    print('Error in creating project: %s' % project)
-    exit(1)
-print("Project '" + project['name'] + "' at '" +
-      project['web_url'] + "' created")
-
-# Allow users with developer access level to push and merge on master
-access_level = 30
-params = {'name': 'master', 'push_access_level': str(
-    access_level), 'merge_access_level': str(access_level)}
-requests.post(base_url + '/projects/' +
-              str(project['id']) + '/protected_branches', params=params, headers=headers).json()
-
-# Add each student as project's developer (level 30)
-for user_id in user_ids:
-    params = {'user_id': user_id, 'access_level': access_level}
-    if args.expires_at:
-        params['expires_at'] = args.expires_at
-    new_user = requests.post(base_url + '/projects/' + str(
-        project['id']) + '/members', params=params, headers=headers).json()
-    if 'message' in new_user:
-        print('Error in adding user: %s' % new_user)
-    else:
-        out = ("Adding '" + new_user['name'] + "' (" + new_user['username'] + ") in '"
-               + project['name'] + "' with access level: " + str(new_user['access_level']))
-        if args.expires_at:
-            out += ", expires at: " + new_user['expires_at']
-        print(out)
-
-# Do not forget : students have to add second remote in their local repositories for pulling last changes.
-- 
GitLab