Abilitare altre API e impostare i file di configurazione

Andiamo su Google Cloud ad abilitare altre API e precisamente queste:

  • Groups Setting API
  • Google Drive API
  • Google Sheet API
  • Google Classroom API

Più avanti avremo la necessità di attivarne altre.

Prima di partire con l’amministrazione vera e propria ho trovato utile creare 4 file nella cartella “venv” che fungono da configurazione generale dell’ambiente di sviluppo. I file sono i seguenti:

auth.py: crea le credenziali di accesso per gli oggetti services, che andremo a vedere successivamente. E’ lo stesso file test.py usato in questa pagina, ma modificato: viene creata una funzione createCreds() che restituisce delle credenziali chiamate appunto creds. Inoltre sono stati aggiunti alcuni scopes che saranno utili per i primi script.

from __future__ import print_function

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build

# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user',
          'https://www.googleapis.com/auth/admin.directory.group',
          'https://www.googleapis.com/auth/apps.groups.settings',
          'https://www.googleapis.com/auth/spreadsheets',
          'https://www.googleapis.com/auth/admin.directory.orgunit.readonly',
          'https://www.googleapis.com/auth/classroom.courses',
          'https://www.googleapis.com/auth/drive.readonly'
         ]


def createCreds():
    """Shows basic usage of the Admin SDK Directory API.
    Prints the emails and names of the first 10 users in the domain.
    """
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.

    #impostare local = True se si lavora in locale, local = False se si lavora in Cloud
    local = True

    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            if local:
                creds = flow.run_local_server(port=0)
            else:
                creds = flow.run_console()
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())
    
    return creds

services.py: è un elenco degli oggetti service, cioè dei componenti dello script che andranno materialmente ad agire sui dati della piattaforma. L’elenco di tutti i possibili oggetti legati alle API di Google si trovano in questa pagina, per il momento ci limitiamo ai quattro qui elencati. Ogni oggetto utilizza il valore creds generato dal file auth.py, che importiamo con la riga import auth e che ci restituisce le credenziali con la riga creds = auth.createCreds(). L’oggetto dedicato ai fogli Google ha una sintassi diversa perché utilizza un modulo Python chiamato gspread che semplifica l’utilizzo della sintassi di utilizzo della API di Google Sheet.

import auth
import gspread
from googleapiclient.discovery import build

creds = auth.createCreds()

adminSrv = build('admin', 'directory_v1', credentials=creds)
groupSrv = build('groupssettings', 'v1', credentials=creds)
classroomSrv = build('classroom', 'v1', credentials=creds)
gspreadSrv = gspread.authorize(creds)

settings.py: è un file che contiene dati che sicuramente verranno utilizzati ripetutamente negli script, come il nostro dominio, il customer Id etc… in questo file trovate anche due dictionary (cioè una struttura dati formata da coppie chiave: valore), uno con i codici meccanografici dei plessi del vostro istituto e uno con le impostazioni da applicare ai gruppi. Quest’ultimo è da lasciare così com’è mentre tutti gli altri dati vanno compilati con i valori esatti inerenti al vostro istituto. Sostituite i meccanografici con quelli reali (quelli di questo file sono inventati) e accanto scrivete il nome più immediato per richiamare il plesso, ad es. un plesso chiamato “Giuseppe Mazzini” diventa “Mazzini”: sarà fondamentale per la creazione dei gruppi.

domain="vostrodominio"

customerId = "vostroCustomerId"

#inserire i veri meccanografici seguiti dai nomi dei plessi
mecs = {
    "SMAA92612Q": "Infanzia",
    "SMEE926165": "Copernico",
    "SMEE926154": "Ungaretti",
    "SMEE926132": "Mazzini",
    "SMEE926143": "Wright",
    "SMEE92612Y": "Magellano",
    "SMMM92612W": "Sonnino"
}

groupSettings = {
    "kind": "groupsSettings#groups",
    "whoCanJoin": "INVITED_CAN_JOIN",
    "whoCanViewMembership": "ALL_IN_DOMAIN_CAN_VIEW",
    "whoCanViewGroup": "ALL_MEMBERS_CAN_VIEW",
    "whoCanInvite": "ALL_MANAGERS_CAN_INVITE",
    "whoCanAdd": "ALL_MANAGERS_CAN_ADD",
    "allowExternalMembers": "false",
    "whoCanPostMessage": "ALL_IN_DOMAIN_CAN_POST",
    "allowWebPosting": "false",
    "primaryLanguage": "it",
    "maxMessageBytes": 26214400,
    "isArchived": "false",
    "archiveOnly": "false",
    "messageModerationLevel": "MODERATE_NONE",
    "spamModerationLevel": "MODERATE",
    "replyTo": "REPLY_TO_SENDER",
    "customReplyTo": "",
    "includeCustomFooter": "false",
    "customFooterText": "",
    "sendMessageDenyNotification": "false",
    "defaultMessageDenyNotificationText": "",
    "showInGroupDirectory": "true",
    "allowGoogleCommunication": "false",
    "membersCanPostAsTheGroup": "false",
    "messageDisplayFont": "DEFAULT_FONT",
    "includeInGlobalAddressList": "true",
    "whoCanLeaveGroup": "NONE_CAN_LEAVE",
    "whoCanContactOwner": "ALL_MEMBERS_CAN_CONTACT",
    "whoCanAddReferences": "NONE",
    "favoriteRepliesOnTop": "true",
    "whoCanModerateMembers": "OWNERS_AND_MANAGERS",
    "whoCanModerateContent": "OWNERS_AND_MANAGERS",
    "whoCanAssistContent": "OWNERS_AND_MANAGERS",
    "customRolesEnabledForSettingsToBeMerged": "false",
    "enableCollaborativeInbox": "false",
    "whoCanDiscoverGroup": "ALL_IN_DOMAIN_CAN_DISCOVER",
    "defaultSender": "DEFAULT_SELF"   
}

utilities.py: è la “cassetta degli attrezzi”, cioè il file che contiene le funzioni che di volta in volta andremo a richiamare negli script. Sono divise in classi, richiamando un po’ la programmazione ad oggetti. Ogni classe è dedicata ad una sezione specifica della piattaforma, al momento ne trovate 3: Gruppi (Group), Utenti (User) e Unità Organizzative (OrgUnit). Ogni funzione è preceduta da un breve commento che illustra il suo scopo.

from services import adminSrv, groupSrv, gmailSrv
import settings
import string
import random
import json

domain = settings.domain

class Group:
    
    #inizializza il gruppo passando come parametro il suo indirizzo email
    def __init__(self, email: str):
        self.email = email
    
    #assegna al gruppo un nome e una descrizione
    def setProperties(self, name, description):
        self.name = name
        self.description = description
    
    #verifica se il gruppo esiste 
    def exists(self):
        try:
            adminSrv.groups().get(groupKey=self.email).execute()
            return True
        except:
            return False
        
    #crea un nuovo gruppo
    def create(self):
        body = {
            'email': self.email,
            'name': self.name,
            'description': self.description
        }
        adminSrv.groups().insert(body=body).execute()
    
    #aggiunge un membro ad un gruppo
    def addMember(self, email):
        body = {
        "email": email,
        "role": "MEMBER",
        "type": "USER",
        "delivery_settings": "ALL_MAIL",
        }
        adminSrv.members().insert(groupKey=self.email, body=body).execute()
    
    #verifica se un utente fa parte di un gruppo
    def hasMemeber(self, member):
        
        membership = adminSrv.members().hasMember(groupKey=self.email, memberKey=member).execute()
        
        return membership['isMember']
        
    #modifica le impostazioni di un gruppo
    def patch(self, settings: dict):
        groupSrv.groups().patch(groupUniqueId=self.email, body=settings).execute()
    
    #elimina un gruppo
    def delete(self):
        adminSrv.groups().delete(groupKey=self.email).execute()
        
    #crea una lista di tutti i gruppi del dominio
    def listAllGroups():
        groupsList = []
        params = {"domain": domain}
        pageToken = None
        while True:
            if pageToken:
                params['pageToken'] = pageToken
            current_page = adminSrv.groups().list(**params).execute()
            groups = current_page.get('groups', [])
            for group in groups:
                groupsList.append(group)
            pageToken = current_page.get('nextPageToken')
            if not pageToken:
                break
        
        return groupsList   

    
class User:

    #inizializza l'oggetto User passando come parametro l'indirizzo email
    def __init__(self, email: str):
        self.email = email
        
    def getSchool(self, code):
        for key, value in settings.mecs.items():
            if code == key:
                self.school = value
    
    #assegna all'oggetto Utente nome, cognome, email (nome.cognome@dominio), unità organizzativa e un parametro "new" per stabilire se è un nuovo utente
    def setProperties(self, name: str, surname: str, orgUnit: str, new: bool, id=None):
        
        email_parts = []
        name_parts = [name, surname]
        for name_part in name_parts:
            part = name_part.lower().replace("à", "a").replace("è", "e").replace("é", "e").replace("ì", "i").replace("ò", "o").replace("ó", "o").replace("ù", "u").replace("'", "").replace("’", "").replace("\"", "").replace(" ", "").replace(";", "").replace(",", "").replace("-", "").replace("'", "")
            email_parts.append(part)
        email = f'{email_parts[0]}.{email_parts[1]}@' + domain

        self.name = name
        self.surname = surname
        self.email = email
        self.orgUnit = orgUnit
        self.new = new
        self.id = id
    
    #verifica se l'id utente è già presente in piattaforma
    def idExists(self, id):
        params = {"domain": domain,
                  "query": "externalId=" + id}
        list = adminSrv.users().list(**params).execute().get("users", [])
        if len(list) == 0:
            return False
        else:
            return True
            
    
    
    def getEmailById(self, id):
        params = {"domain": domain,
                  "query": "externalId=" + id}
        user = adminSrv.users().list(**params).execute()        
        self.email = user['users'][0]['emails'][0]['address']
        
    #verifica se l'email dell'utente è già presente in piattaforma
    def emailExists(self):
        try:
            adminSrv.users().get(userKey=self.email).execute()
            return True
        except:
            return False
      
    #se l'indirizzo è già esistente, aggiunge un numero dopo il cognome a partire da 1 ed effettua ricorsivamente la verifica del nuovo indirizzo email  
    def checkEmail(self, count=0):
        if not self.emailExists():
            return self.email
        else:
            count += 1
            if count == 1:
                self.email = self.email.replace('@', str(count)+'@')
            else:
                self.email = self.email.replace(str(count-1), str(count))
            return self.checkEmail(count)    
    
    #genera una password
    def createPassword(self):

        # imposta lunghezza password
        length = 8

        # determina i set di caratteri 
        lower = string.ascii_lowercase
        upper = string.ascii_uppercase
        num = string.digits
        #symbols = string.punctuation

        # unisci i set di caratteri nella variabile all
        all = lower + upper + num

        # scegli casualmente i caratteri da all
        temp = random.sample(all, length)

        # unisci i caratteri scelti casualmente
        password = "".join(temp)

        # restituisci la password
        return password
    
    #inserisce un utente in piattaforma. Se non si passa l'argomento password, verrà generata automaticamente
    def create(self, password=None):
        if password == None:
            password = self.createPassword()
        body = {
            "changePasswordAtNextLogin": True,
            "isAdmin": False,
            "name": {
                "familyName": self.surname.upper(),
                "givenName": self.name.upper(),
            },
            "orgUnitPath": self.orgUnit,
            "password": password,
            "primaryEmail": self.email,
            "suspended": False
        }
        if self.id:
            externalIds = {"value": self.id,
                           "type": "organization"}
            body["externalIds"] = [externalIds]
        adminSrv.users().insert(body=body).execute()
    
    #aggiorna i dati dell'utente passando un parametro dictionary    
    def patch(self, settings: dict):
        adminSrv.users().patch(userKey=self.email, body=settings).execute()
    
    #restituisce tutte le informazioni dell'utente. Se formatted è impostato su True, verrano restituire in formato JSON   
    def getInfo(self, formatted=False):
        info = adminSrv.users().get(userKey=self.email).execute()
        
        if formatted == True:
            info_f = json.dumps(info, indent=4)
            return info_f
        
        return info
    
    #imposta l'id dell'utente
    def setUserId(self, id):
        settings = {
            "externalIds": [
                {
                    "value": id,
                    "type": "organization"
                }
            ]
        }
        self.patch(settings)
    
    #ottiene l'id dell'utente
    def getUserId(self):
        infos = self.getInfo()
        id = infos["externalIds"][0]["value"]
        
        return id
    
    def isSuspended(self):
        infos = self.getInfo()
        suspended = infos["suspended"]
        
        return suspended
    
    #elimina l'utente
    def delete(self):
        adminSrv.users().delete(userKey=self.email).execute()
    
    #restituisce una lista di tutti gli utenti del dominio
    def listAllUsers(self):
        usersList = []
        params = {"domain": domain}
        pageToken = None
        while True:
            if pageToken:
                params['pageToken'] = pageToken
            current_page = adminSrv.users().list(**params).execute()
            users = current_page.get('users', [])
            for user in users:
                usersList.append(user)
            pageToken = current_page.get('nextPageToken')
            if not pageToken:
                break
        
        return usersList

class OrgUnit:
    
    #restituisce una lista di tutte le unità organizzative del dominio
    def listAllOrgUnits(self):
        orgUnitsList = adminSrv.orgunits().list(customerId=settings.customerId, orgUnitPath="/", type='all').execute().get('organizationUnits', {})
        
        return orgUnitsList
    
    #restituisce il percorso dell'unità organizzativa passando come parametro il suo nome
    def getOrgUnitPathByName(self, name: str):
        orgUnitsList = self.listAllOrgUnits()
        for orgUnit in orgUnitsList:
            if orgUnit['name'] == name:
                
                return orgUnit['orgUnitPath']
         

Una volta inseriti questi file nella nostra cartella “venv” andiamo ad effettuare la prima operazione in piattaforma, cioè gestire gli inserimenti dei nuovi studenti