From 24ff1c5e0ff4734f4cb1ce7f854dd4ba6cab2f25 Mon Sep 17 00:00:00 2001
From: Chris Bielow <chris.bielow@fu-berlin.de>
Date: Tue, 23 Oct 2018 08:37:04 +0200
Subject: [PATCH] a version that works, if you are willing to accept some
 documented quirks

---
 main.py           | 92 +++++++++++++++++++++++++++----------------
 manageprojects.py | 48 +++++++++++++++--------
 projectassign.py  | 69 +++++++++++++++++++++------------
 students.list     | 99 +++++++++++++++++++++++++++++++++++++++++++++--
 4 files changed, 229 insertions(+), 79 deletions(-)

diff --git a/main.py b/main.py
index 5ad26fb..1208182 100644
--- a/main.py
+++ b/main.py
@@ -2,66 +2,90 @@ import requests
 import json
 import manageprojects
 import projectassign
+import os
 
-token = "$GITLAB_ACCESS_TOKEN"
+token = os.getenv('GITLABTOKEN', None)
 apilink = "https://git.imp.fu-berlin.de/api/v4/"
 group_name = None
 
-def getgroupid(apilink, token, group_name):
+def getgroupid(group_name):
 
-    rawdata = requests.get(apilink + "/groups?private_token=" + token, verify = False)
+    rawdata = requests.get(apilink + "/groups?private_token=" + token, verify = True)
     data = json.loads(rawdata.content)
 
     group_id = None
-
-    for i in range(0,len(data)):
-        if(data[i]["name"] == group_name):
-            group_id = data[i]["id"]
+    #print(data);
+    #print (type(data), "\n")
+    
+    for d in data:
+        if ("name" in d and d["name"] == group_name):
+            group_id = d["id"] # int
             break
-
+    if (group_id is None):
+        print("Could not find group. Response was: ", data)
+    else:
+        print("Found group id: '", group_id ,"'\n")
     return group_id
 
 def main():
 
     print("Willkommen im GitLab Projektmanagement\n")
 
-    group_name = input("Welche Gruppe soll bearbeitet werden? ")
-    group_id = getgroupid(apilink, token, group_name)
+    if (not token):
+        raise Exception("Invalid authentication token: Set $GITLABTOKEN in your environment!")
+    group_id = None
+    group_name_default = "adp2018";
+    while (group_id is None):
+        group_name = input("Welche Gruppe soll bearbeitet werden (Enter = " + group_name_default + ")? ")
+        if (not group_name):
+            group_name = group_name_default
+        group_id = getgroupid(group_name)
 
     while(True):
 
-        print("\ncr - Projekte erstellen")
-        print("rm - ein Projekt löschen")
-        print("as - Studenten zu Projekten hinzufügen\n")
-
-        eingabe = input("Eingabe: ")
-
+        print("\n")
+        print("ls - list projects")
+        print("cr - create projects")
+        print("rm - remove projects")
+        print("as - assign students to existing projects\n")
+
+        
+        eingabe = input("choice: ")
+        
+        if (eingabe == "ls"):
+            proj_name_prefix = input("What is the project prefix? (Enter = all projects) ")
+            projects = manageprojects.getprojects(apilink, token, group_id, proj_name_prefix)
+    
+        
         if(eingabe == "cr"):
-            count = input("\nwie viele Projekte sollen erstellt werden?: ")
-            name = input("wie sollen die Projekte heißen?: ")
-            sf = input("\nSollen wirklich " + count + " Projekte mit dem Namen " + name + " erstellt werden? (y/n): ")
+            count = input("\nHow many projects do you need?: ")
+            name = input("What is the project prefix (e.g. 'group')?: ")
+            sf = input("\nDo you really want 1.." + count + " projects with prefix '" + name + "'(if the projects already exist, nothing will happen)? (y/n): ")
 
             if(sf == "y" or sf == "yes"):
-                for count in range(0, int(count)):
-                    manageprojects.create(apilink, token, name , count, group_id)
-                    print(count + " Projekte wurden erstellt")
+                for c in range(0, int(count)):
+                    manageprojects.create(apilink, token, group_id, name, c)
+                print(str(count) + " projects created")
 
         if(eingabe == "rm"):
-            name = input("\nwelches Projekt soll gelöscht werden?: ")
-
-            proj_id = manageprojects.getprojectid(apilink, group_id, name, token)
-            sf = input("Soll das Projekt " + name + " mit der ID " + str(proj_id) + " wirklich gelöscht werden? (y/n): ")
+            proj_name_prefix = input("What is the project prefix (e.g. 'group')? (Enter = all projects) ")
+            projects = manageprojects.getprojects(apilink, token, group_id, proj_name_prefix)
+            
+            sf = input("Do you really want to delete those? (y/n): ")
             if(sf == "y" or sf == "yes"):
+                for p in projects:
+                    proj_id = manageprojects.getprojectid(apilink, token, group_id, p.name)
                     manageprojects.remove(apilink, token, group_id, proj_id)
-                    print("Das Projekt " + name + " wurde gelöscht")
-_
+                    print("Project " + p.name + " removed\n")
+
         if(eingabe == "as"):
-            projectnamespace = input("Wie heißen die Projekte?")
-            studentlist = input("Aus welcher Liste sollen Nutzer hinzugefügt werden?")
-            count = input("Wie viele Nutzer sollen pro Projekt verteilt werden?")
-            access_level = input("Access-Level?\n10 => Guest access\n20 => Reporter access\n30 => Developer access\n40 => Maintainer access")
-            group_id = str(getgroupid(apilink, token, group_name))
-            projectassign.assign(projectnamespace, group_id, studentlist, count, access_level, apilink, token)
+            student_list = input("Aus welcher Liste sollen Nutzer hinzugefügt werden? (Enter = students.list)")
+            if (not student_list):
+                student_list = "students.list"
+            access_level = input("Access-Level?\n10 => Guest access\n20 => Reporter access\n30 => Developer access (no push to master)\n40 => Maintainer access (Enter = Maintainer)")
+            if (not access_level):
+                access_level = 40;
+            projectassign.assign(apilink, token, group_name, student_list, access_level)
 
 
 main()
diff --git a/manageprojects.py b/manageprojects.py
index 3936b46..a06b889 100644
--- a/manageprojects.py
+++ b/manageprojects.py
@@ -5,37 +5,51 @@ class project:
     id = 0
     name = ""
 
-def create(apilink, token, name, count, groupid):
-
-    requests.post(apilink+"/projects?private_token="+token+"&name="+name+"_"+str(count+1)+"&namespace_id="+str(groupid),verify=False)
-
+def create(apilink, token, groupid, name, count):
+    fullname = name + str(count+1)
+    print ("create project: " + fullname + "\n")
+    requests.post(apilink + "/projects?private_token=" + token 
+                                   + "&name=" + fullname
+                                   + "&namespace_id=" + str(groupid),
+                  verify = True)
+
+                  
 def remove(apilink, token, groupid, projid):
+    requests.delete(apilink+"/projects/"+str(projid)+"?private_token="+token+"&namespace_id="+str(groupid), verify = True)
 
-    requests.delete(apilink+"/projects/"+str(projid)+"?private_token="+token+"&namespace_id="+str(groupid),verify=False)
 
-def getprojects(projectnamespace, apilink, groupid, token):
 
-    rawdata = requests.get(apilink+"/groups/"+str(groupid)+"/projects?private_token="+token, verify = False)
+def getprojects(apilink, token, groupid, proj_name_prefix):
+    print("warning: listing projects by Searching for a name prefix is broken in Gitlab, since the response will be incomplete for large answers. You might need to iterate (for deleting) or find a more specific prefix (which finds less hits)")
+    rawdata = requests.get(apilink+"/groups/"+str(groupid)+"/projects?private_token="+token, verify = True)
     data = json.loads(rawdata.content)
     projects = []
     for i in range(0,len(data)):
-        tempname = data[i]["name"].split("_")[0]
-        if(tempname == projectnamespace):
+        if (data[i]["name"].startswith(proj_name_prefix)):
             prodata = project()
             prodata.id = data[i]["id"]
             prodata.name = data[i]["name"]
             projects.append(prodata)
 
+    print("Found projects: " + ", ".join([p.name for p in projects]))
+            
     return projects
 
-def getprojectid(apilink, groupid, name, token):
 
-    rawdata = requests.get(apilink+"/groups/"+str(groupid)+"/projects?private_token="+token, verify = False)
-    data = json.loads(rawdata.content)
+def getprojectid(apilink, token, group_name, proj_name):
+    # do NOT use the general query below, because it will prematurely stop after some records (some limitation/bug in the API)
+    # this is problematic if you have a dozen or so groups
+    #rawdata = requests.get(apilink+"/groups/" + str(groupid) + "/projects?private_token=" + token, verify = True)
 
-    for i in range(0,len(data)):
-        if(data[i]["name"] == name):
-            projid = data[i]["id"]
-            break
+    # query project directly using groupname/project (the / must be encoded as %2F)
+    rawdata = requests.get(apilink + "/projects/" + str(group_name) + "%2F" + proj_name + "?private_token=" + token, verify = True)
+    data = json.loads(rawdata.content)
 
-    return projid
+    proj_id = None
+    if ("name" in data and data["name"] == proj_name):
+        proj_id = data["id"]
+        print("Found project id: ", proj_id)
+    else:
+        print("Could not find project '" + proj_name + "'.") # Response was: ", data)
+        
+    return proj_id
diff --git a/projectassign.py b/projectassign.py
index e59a3d1..96ecff2 100644
--- a/projectassign.py
+++ b/projectassign.py
@@ -3,32 +3,53 @@ import json
 import manageprojects
 
 class member:
-    id = 0
-    name = ""
+    gitlab_group = ""
+    zedat_name = ""
 
-def readstudentlist(studentlist):
-    list = open(studentlist + ".txt")
-    students = list.read().splitlines()
+
+    
+def readStudentList(file_students):
+    list = open(file_students)
+    lines = list.read().splitlines()
+    students = []
+    for l in lines:
+        itm = l.split('\t')
+        if len(itm)!=2:
+            raise Exception("Invalid student: '" + l + "'. Must have two items separated by tab!")
+        s = member()
+        s.zedat_name = itm[0]
+        s.gitlab_group = itm[1]
+        students.append(s)
     return students
 
-def getstudentid(student, apilink, token):
-    rawdata = requests.get(apilink + "/users?private_token=" + token+ "&username=" + student, verify=False)
+def getStudentID(apilink, token, zedat_name):
+    rawdata = requests.get(apilink + "/users?private_token=" + token + "&username=" + zedat_name, verify = True)
     data = json.loads(rawdata.content)
-    return data[0]["id"]
+    s_id = None
+    for d in data:
+        if ("id" in d):
+            s_id = d["id"]
+            break
+    if (s_id is None):
+        print("Could not find student '" + zedat_name + "'.") # Response was: ", data)
+    else:
+        print("Found student id: ", s_id ,"\n")
+        
+    return s_id
 
-def assign(projectnamespace, groupid, studentlist, studentsamount, a_level, apilink, token):
-    students = readstudentlist(studentlist)
-    projects = manageprojects.getprojects(projectnamespace, apilink, groupid, token)
-    studentindex = 0
-    print("im alive")
-    print(len(projects))
-    for p in range(0,len(projects)):
-        print("project" + str(p))
-        for s in range(0,int(studentsamount)):
-            if(studentindex == len(students)):
-                break
-            studentid = getstudentid(students[studentindex], apilink, token)
-            print(projects[p].id)
-            print(studentid)
-            requests.post(apilink + "/projects/" + str(projects[p].id) + "/members?private_token=" + token + "&user_id=" + str(studentid) + "&access_level=" + str(a_level), verify = False)
-            studentindex += 1
+def assign(apilink, token, group_name, file_students, access_level):
+    students = readStudentList(file_students)
+    for s in students:
+        print("\n-- Student " + s.zedat_name + " in " + s.gitlab_group)
+        proj_id = manageprojects.getprojectid(apilink, token, group_name, s.gitlab_group)
+        if (proj_id is None):
+            exit("Group is unknown. Please create it or correct its name")      # this is a hard error -- groups are not created correctly
+        student_id = getStudentID(apilink, token, s.zedat_name)
+        if (student_id is None):
+            continue
+        # creates member (does nothing if already exists)
+        requests.post(apilink + "/projects/" + str(proj_id) + "/members?private_token=" + token + "&user_id=" + str(student_id) + "&access_level=" + str(access_level), verify = True)
+        # does an update (e.g. access rights, in case the student already exists)
+        link = apilink + "/projects/" + str(proj_id) + "/members/" + str(student_id) + "?private_token=" + token + "&access_level=" + str(access_level);
+        requests.put(link, verify = True)
+        print("   --> added")
diff --git a/students.list b/students.list
index 5e4978b..8db96dc 100644
--- a/students.list
+++ b/students.list
@@ -1,4 +1,95 @@
-student1
-student2
-student3
-student4
+farab95	group1
+sofyal97	group1
+felid99	group2
+mellej98	group2
+simonsasse97	group2
+armaj97	group3
+koehlek99	group3
+hannas24	group3
+bauman98	group4
+anthof95	group4
+glim99	group4
+oezgenug94	group5
+risa0	group5
+henris07	group5
+daboj85	group6
+tetak94	group6
+lapuj92	group6
+sarahjuf94	group7
+dennik99	group7
+ninao99	group7
+kruschwia32	group8
+grem16	group8
+anniepham	group8
+chantak98	group9
+julikaw96	group9
+juliaz00	group9
+dilad97	group10
+hossnim96	group10
+adim99	group10
+miaa99	group11
+bannwid98	group11
+laraj98	group11
+finnjab97	group12
+emi41	group12
+tstrunk	group12
+semiaj97	group13
+panc97	group13
+irfay03	group13
+hessea68	group14
+kolby19	group14
+transat	group14
+warda06	group15
+helenabraun	group15
+on3yhwh	group15
+melim09	group16
+sielatchom00	group16
+schiris15	group16
+gtienle	group17
+goekce	group17
+sebastiann	group17
+bieniam98	group18
+salomok98	group18
+sprangel99	group18
+tomlukas15	group19
+sebpaltzer	group19
+ruyog96	group19
+ignaciocalvi	group20
+koesed96	group20
+pipaj97	group20
+ashkag98	group21
+mustah98	group21
+trav97	group21
+misevio93	group22
+tumanova	group22
+chex98	group22
+camilavib00	group23
+muehlhaup97	group23
+felir69	group23
+herbste93	group24
+jschmacht	group24
+victorsi	group24
+bodul99	group25
+lenaedelmann	group25
+pohll95	group25
+aakan96	group26
+giacuop96	group26
+hendriz98	group26
+pikkin87	group27
+ammaa97	group28
+mmertins	group28
+wangw98	group28
+namuub98	group29
+karadam96	group29
+koirap93	group29
+kriea97	group30
+ponzej97	group30
+ingat98	group30
+yunusek77	group31
+yusras98	group31
+dimit98	group31
+maximilian45	group32
+lenzo99	group33
+christiap20	group33
+tinop99	group33
+gachc96	group34
-- 
GitLab