Initial commit
This commit is contained in:
commit
536ff43ce4
158 files changed
+30355
No files matched your search
@@ -0,0 +1,169 @@
|
||||
"""
|
||||
Overzicht van alle lopende e-mailmachtigingen (Gmail-delegaties) van de school.
|
||||
|
||||
Google heeft geen lijst "alle delegaties van het domein": je kan ze enkel per mailbox opvragen
|
||||
(users.settings.delegates.list, in naam van de eigenaar). Daarom loopt er een achtergrondtaak die
|
||||
alle actieve (niet-geschorste) gebruikers afgaat, met een paar parallelle verzoeken.
|
||||
Het resultaat komt in de tabel gmail_delegaties en wordt:
|
||||
- volledig vernieuwd met de knop op de pagina, of vanzelf als het ouder is dan VERVERS_UREN;
|
||||
- meteen bijgewerkt als in PIVOT een machtiging toegevoegd of ingetrokken wordt, of als je een mailbox opent.
|
||||
"""
|
||||
import sys
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from datetime import datetime
|
||||
|
||||
from database import execute_query
|
||||
from tenancy import school_singleton, met_school, huidige_school, in_school
|
||||
|
||||
VERVERS_UREN = 24
|
||||
PARALLEL = 8
|
||||
|
||||
|
||||
class DelegatieOverzicht:
|
||||
def __new__(cls):
|
||||
def init(inst):
|
||||
inst._lock = threading.Lock()
|
||||
inst.status = {'bezig': False, 'voortgang': 0, 'totaal': 0, 'fout': None, 'resultaat': None}
|
||||
return school_singleton(cls, init)
|
||||
|
||||
@staticmethod
|
||||
def ensure_table():
|
||||
execute_query("""
|
||||
CREATE TABLE IF NOT EXISTS gmail_delegaties (
|
||||
Mailbox VARCHAR(255) NOT NULL,
|
||||
Mailbox_naam VARCHAR(255) NULL,
|
||||
Gedelegeerde VARCHAR(255) NOT NULL,
|
||||
Gedelegeerde_naam VARCHAR(255) NULL,
|
||||
Status VARCHAR(20) NULL,
|
||||
Opgehaald DATETIME NOT NULL,
|
||||
PRIMARY KEY (Mailbox, Gedelegeerde),
|
||||
KEY idx_gmail_deleg_gedelegeerde (Gedelegeerde)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""", commit=True)
|
||||
execute_query("CREATE TABLE IF NOT EXISTS gmail_delegaties_scan (ID TINYINT NOT NULL PRIMARY KEY, "
|
||||
"Opgehaald DATETIME NULL, Mailboxen INT NULL, Fouten INT NULL) ENGINE=InnoDB", commit=True)
|
||||
|
||||
# ---------------- opvragen ----------------
|
||||
def alle(self):
|
||||
return execute_query("SELECT * FROM gmail_delegaties ORDER BY Mailbox, Gedelegeerde") or []
|
||||
|
||||
def laatst_opgehaald(self):
|
||||
rij = execute_query("SELECT Opgehaald, Mailboxen, Fouten FROM gmail_delegaties_scan WHERE ID = 1")
|
||||
return rij[0] if rij else None
|
||||
|
||||
def verouderd(self):
|
||||
laatst = self.laatst_opgehaald()
|
||||
return not laatst or not laatst['Opgehaald'] or \
|
||||
(datetime.now() - laatst['Opgehaald']).total_seconds() > VERVERS_UREN * 3600
|
||||
|
||||
# ---------------- één mailbox bijwerken (na toevoegen/intrekken/openen in PIVOT) ----------------
|
||||
def bewaar_mailbox(self, mailbox, naam, delegates, namen=None):
|
||||
"""Vervangt de rijen van één mailbox door de opgegeven gedelegeerden [{'email','status'}]."""
|
||||
try:
|
||||
self.ensure_table()
|
||||
# Namen die we al kennen (uit de volledige ophaling) behouden
|
||||
bekend = {}
|
||||
for r in execute_query("SELECT Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam FROM gmail_delegaties") or []:
|
||||
if r['Mailbox_naam']:
|
||||
bekend[r['Mailbox'].lower()] = r['Mailbox_naam']
|
||||
if r['Gedelegeerde_naam']:
|
||||
bekend[r['Gedelegeerde'].lower()] = r['Gedelegeerde_naam']
|
||||
namen = {**bekend, **(namen or {})}
|
||||
execute_query("DELETE FROM gmail_delegaties WHERE Mailbox = %s", (mailbox,), commit=True)
|
||||
for d in delegates:
|
||||
execute_query("INSERT INTO gmail_delegaties (Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam, "
|
||||
"Status, Opgehaald) VALUES (%s, %s, %s, %s, %s, NOW())",
|
||||
(mailbox, naam, d['email'], namen.get(d['email'].lower()) or d.get('naam'),
|
||||
d.get('status')), commit=True)
|
||||
except Exception as e:
|
||||
sys.stderr.write(f"Delegaties: mailbox {mailbox} niet bewaard: {e}\n")
|
||||
|
||||
# ---------------- alles ophalen uit Google ----------------
|
||||
def _gebruikers(self, directory):
|
||||
uit, token = [], None
|
||||
while True:
|
||||
resp = directory.users().list(customer='my_customer', maxResults=500, pageToken=token,
|
||||
fields='nextPageToken,users(primaryEmail,name/fullName,suspended)').execute()
|
||||
for u in resp.get('users', []):
|
||||
uit.append({'email': u['primaryEmail'].lower(), 'naam': (u.get('name') or {}).get('fullName', ''),
|
||||
'geschorst': bool(u.get('suspended'))})
|
||||
token = resp.get('nextPageToken')
|
||||
if not token:
|
||||
return uit
|
||||
|
||||
def sync(self):
|
||||
if not self._lock.acquire(blocking=False):
|
||||
return False, "Er loopt al een ophaling."
|
||||
try:
|
||||
from services.gmail_delegation_service import GmailDelegationService, DelegationError
|
||||
self.status.update(bezig=True, voortgang=0, totaal=0, fout=None, resultaat=None)
|
||||
self.ensure_table()
|
||||
svc = GmailDelegationService()
|
||||
gebruikers = self._gebruikers(svc._directory_api())
|
||||
namen = {u['email']: u['naam'] for u in gebruikers}
|
||||
actief = [u for u in gebruikers if not u['geschorst']]
|
||||
self.status['totaal'] = len(actief)
|
||||
school = huidige_school()
|
||||
fouten, rechtenfout, gelukt = [], [], []
|
||||
|
||||
def een(u):
|
||||
# Krijgt Google telkens dezelfde rechtenfout, dan niet alle gebruikers blijven proberen
|
||||
if len(rechtenfout) >= 3 and not gelukt:
|
||||
self.status['voortgang'] += 1
|
||||
return u, None
|
||||
with in_school(school):
|
||||
try:
|
||||
delegates = svc.list_delegates(u['email'])
|
||||
gelukt.append(1)
|
||||
return u, delegates
|
||||
except DelegationError as e:
|
||||
tekst = str(e)
|
||||
if 'serviceaccount mist een Gmail-recht' in tekst or 'Gmail API staat nog niet aan' in tekst:
|
||||
rechtenfout.append(tekst)
|
||||
fouten.append(u['email'])
|
||||
return u, None
|
||||
except Exception:
|
||||
fouten.append(u['email'])
|
||||
return u, None
|
||||
finally:
|
||||
self.status['voortgang'] += 1
|
||||
|
||||
resultaten = []
|
||||
with ThreadPoolExecutor(max_workers=PARALLEL) as pool:
|
||||
for u, delegates in pool.map(een, actief):
|
||||
if delegates:
|
||||
resultaten.append((u, delegates))
|
||||
if rechtenfout and not gelukt:
|
||||
raise RuntimeError(rechtenfout[0])
|
||||
|
||||
nu = datetime.now()
|
||||
execute_query("DELETE FROM gmail_delegaties", commit=True)
|
||||
aantal = 0
|
||||
for u, delegates in resultaten:
|
||||
for d in delegates:
|
||||
execute_query("INSERT INTO gmail_delegaties (Mailbox, Mailbox_naam, Gedelegeerde, Gedelegeerde_naam, "
|
||||
"Status, Opgehaald) VALUES (%s, %s, %s, %s, %s, %s)",
|
||||
(u['email'], u['naam'], d['email'], namen.get(d['email'].lower()), d['status'], nu),
|
||||
commit=True)
|
||||
aantal += 1
|
||||
execute_query("INSERT INTO gmail_delegaties_scan (ID, Opgehaald, Mailboxen, Fouten) VALUES (1, %s, %s, %s) "
|
||||
"ON DUPLICATE KEY UPDATE Opgehaald = VALUES(Opgehaald), Mailboxen = VALUES(Mailboxen), "
|
||||
"Fouten = VALUES(Fouten)", (nu, len(actief), len(fouten)), commit=True)
|
||||
msg = f"{len(actief)} mailboxen nagekeken: {aantal} machtiging(en) bij {len(resultaten)} mailbox(en)."
|
||||
if fouten:
|
||||
msg += f" {len(fouten)} mailbox(en) konden niet bevraagd worden (bv. zonder Gmail-licentie)."
|
||||
self.status['resultaat'] = msg
|
||||
return True, msg
|
||||
except Exception as e:
|
||||
self.status['fout'] = str(e)
|
||||
return False, f"Ophalen mislukt: {e}"
|
||||
finally:
|
||||
self.status['bezig'] = False
|
||||
self._lock.release()
|
||||
|
||||
def sync_op_achtergrond(self):
|
||||
if self.status['bezig']:
|
||||
return False
|
||||
self.status.update(bezig=True, voortgang=0, totaal=0, fout=None)
|
||||
threading.Thread(target=met_school(self.sync), daemon=True).start()
|
||||
return True
|
||||
Reference in new issue
Block a user