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