forked from ScoDoc/ScoDoc
Conservation des anciens id de semestres et d'étudiants pour accès aux archives.
This commit is contained in:
parent
41c4fdafa4
commit
58d6db57fa
@ -150,6 +150,8 @@ Mémo pour développeurs: séquence re-création d'une base:
|
|||||||
|
|
||||||
# Paquet debian 11
|
# Paquet debian 11
|
||||||
|
|
||||||
Ce que le script d'installation/mise à jour du paquet ne fait pas encore:
|
Les scripts associés au paquet Debian (.deb) sont dans `tools/debian`.
|
||||||
|
|
||||||
|
La préparation d'une release se fait à l'aide du script
|
||||||
|
`tools/build_release.sh`.
|
||||||
|
|
||||||
- migrations flask (à faire)
|
|
||||||
|
@ -40,6 +40,8 @@ class Identite(db.Model):
|
|||||||
# Codes INE et NIP pas unique car le meme etud peut etre ds plusieurs dept
|
# Codes INE et NIP pas unique car le meme etud peut etre ds plusieurs dept
|
||||||
code_nip = db.Column(db.Text())
|
code_nip = db.Column(db.Text())
|
||||||
code_ine = db.Column(db.Text())
|
code_ine = db.Column(db.Text())
|
||||||
|
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||||
|
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||||
|
|
||||||
|
|
||||||
class Adresse(db.Model):
|
class Adresse(db.Model):
|
||||||
|
@ -69,6 +69,8 @@ class FormSemestre(db.Model):
|
|||||||
etapes = db.relationship(
|
etapes = db.relationship(
|
||||||
"NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre"
|
"NotesFormsemestreEtape", cascade="all,delete", backref="notes_formsemestre"
|
||||||
)
|
)
|
||||||
|
# Ancien id ScoDoc7 pour les migrations de bases anciennes
|
||||||
|
scodoc7_id = db.Column(db.Text(), nullable=True)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(FormSemestre, self).__init__(**kwargs)
|
super(FormSemestre, self).__init__(**kwargs)
|
||||||
|
@ -34,9 +34,11 @@
|
|||||||
|
|
||||||
Les PV de jurys et documents associés sont stockées dans un sous-repertoire de la forme
|
Les PV de jurys et documents associés sont stockées dans un sous-repertoire de la forme
|
||||||
<archivedir>/<dept>/<formsemestre_id>/<YYYY-MM-DD-HH-MM-SS>
|
<archivedir>/<dept>/<formsemestre_id>/<YYYY-MM-DD-HH-MM-SS>
|
||||||
|
(formsemestre_id est ici FormSemestre.scodoc7_id ou à défaut FormSemestre.id)
|
||||||
|
|
||||||
Les documents liés à l'étudiant sont dans
|
Les documents liés à l'étudiant sont dans
|
||||||
<archivedir>/docetuds/<dept>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
<archivedir>/docetuds/<dept>/<etudid>/<YYYY-MM-DD-HH-MM-SS>
|
||||||
|
(etudid est ici soit Identite.scodoc7id, soit à défaut Identite.id)
|
||||||
|
|
||||||
Les maquettes Apogée pour l'export des notes sont dans
|
Les maquettes Apogée pour l'export des notes sont dans
|
||||||
<archivedir>/apo_csv/<dept>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
<archivedir>/apo_csv/<dept>/<annee_scolaire>-<sem_id>/<YYYY-MM-DD-HH-MM-SS>/<code_etape>.csv
|
||||||
@ -284,7 +286,9 @@ def do_formsemestre_archive(
|
|||||||
"""
|
"""
|
||||||
from app.scodoc.sco_recapcomplet import make_formsemestre_recapcomplet
|
from app.scodoc.sco_recapcomplet import make_formsemestre_recapcomplet
|
||||||
|
|
||||||
archive_id = PVArchive.create_obj_archive(formsemestre_id, description)
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
|
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||||
|
archive_id = PVArchive.create_obj_archive(sem_archive_id, description)
|
||||||
date = PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
|
date = PVArchive.get_archive_date(archive_id).strftime("%d/%m/%Y à %H:%M")
|
||||||
|
|
||||||
if not group_ids:
|
if not group_ids:
|
||||||
@ -397,7 +401,8 @@ def formsemestre_archive(REQUEST, formsemestre_id, group_ids=[]):
|
|||||||
les documents résultant de ce semestre: PV de jury, lettres individuelles,
|
les documents résultant de ce semestre: PV de jury, lettres individuelles,
|
||||||
tableaux récapitulatifs.</p><p class="help">Les documents archivés sont
|
tableaux récapitulatifs.</p><p class="help">Les documents archivés sont
|
||||||
enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
||||||
</p><p class="help">On peut archiver plusieurs versions des documents (avant et après le jury par exemple).
|
</p><p class="help">On peut archiver plusieurs versions des documents
|
||||||
|
(avant et après le jury par exemple).
|
||||||
</p>
|
</p>
|
||||||
""",
|
""",
|
||||||
]
|
]
|
||||||
@ -495,8 +500,10 @@ enregistrés et non modifiables, on peut les retrouver ultérieurement.
|
|||||||
|
|
||||||
def formsemestre_list_archives(REQUEST, formsemestre_id):
|
def formsemestre_list_archives(REQUEST, formsemestre_id):
|
||||||
"""Page listing archives"""
|
"""Page listing archives"""
|
||||||
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
|
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||||
L = []
|
L = []
|
||||||
for archive_id in PVArchive.list_obj_archives(formsemestre_id):
|
for archive_id in PVArchive.list_obj_archives(sem_archive_id):
|
||||||
a = {
|
a = {
|
||||||
"archive_id": archive_id,
|
"archive_id": archive_id,
|
||||||
"description": PVArchive.get_archive_description(archive_id),
|
"description": PVArchive.get_archive_description(archive_id),
|
||||||
@ -505,7 +512,6 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
|||||||
}
|
}
|
||||||
L.append(a)
|
L.append(a)
|
||||||
|
|
||||||
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
|
||||||
H = [html_sco_header.html_sem_header(REQUEST, "Archive des PV et résultats ", sem)]
|
H = [html_sco_header.html_sem_header(REQUEST, "Archive des PV et résultats ", sem)]
|
||||||
if not L:
|
if not L:
|
||||||
H.append("<p>aucune archive enregistrée</p>")
|
H.append("<p>aucune archive enregistrée</p>")
|
||||||
@ -537,7 +543,9 @@ def formsemestre_list_archives(REQUEST, formsemestre_id):
|
|||||||
|
|
||||||
def formsemestre_get_archived_file(REQUEST, formsemestre_id, archive_name, filename):
|
def formsemestre_get_archived_file(REQUEST, formsemestre_id, archive_name, filename):
|
||||||
"""Send file to client."""
|
"""Send file to client."""
|
||||||
return PVArchive.get_archived_file(REQUEST, formsemestre_id, archive_name, filename)
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
|
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||||
|
return PVArchive.get_archived_file(REQUEST, sem_archive_id, archive_name, filename)
|
||||||
|
|
||||||
|
|
||||||
def formsemestre_delete_archive(
|
def formsemestre_delete_archive(
|
||||||
@ -548,8 +556,9 @@ def formsemestre_delete_archive(
|
|||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||||
)
|
)
|
||||||
_ = sco_formsemestre.get_formsemestre(formsemestre_id) # check formsemestre_id
|
sem = sco_formsemestre.get_formsemestre(formsemestre_id)
|
||||||
archive_id = PVArchive.get_id_from_name(formsemestre_id, archive_name)
|
sem_archive_id = sem["scodoc7_id"] or formsemestre_id
|
||||||
|
archive_id = PVArchive.get_id_from_name(sem_archive_id, archive_name)
|
||||||
|
|
||||||
dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id)
|
dest_url = "formsemestre_list_archives?formsemestre_id=%s" % (formsemestre_id)
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ from app.scodoc import sco_trombino
|
|||||||
from app.scodoc import sco_excel
|
from app.scodoc import sco_excel
|
||||||
from app.scodoc import sco_archives
|
from app.scodoc import sco_archives
|
||||||
from app.scodoc.sco_permissions import Permission
|
from app.scodoc.sco_permissions import Permission
|
||||||
from app.scodoc.sco_exceptions import AccessDenied
|
from app.scodoc.sco_exceptions import AccessDenied, ScoValueError
|
||||||
from app.scodoc.TrivialFormulator import TrivialFormulator
|
from app.scodoc.TrivialFormulator import TrivialFormulator
|
||||||
from app.scodoc import html_sco_header
|
from app.scodoc import html_sco_header
|
||||||
from app.scodoc import sco_etud
|
from app.scodoc import sco_etud
|
||||||
@ -61,8 +61,13 @@ def can_edit_etud_archive(authuser):
|
|||||||
def etud_list_archives_html(REQUEST, etudid):
|
def etud_list_archives_html(REQUEST, etudid):
|
||||||
"""HTML snippet listing archives"""
|
"""HTML snippet listing archives"""
|
||||||
can_edit = can_edit_etud_archive(REQUEST.AUTHENTICATED_USER)
|
can_edit = can_edit_etud_archive(REQUEST.AUTHENTICATED_USER)
|
||||||
|
etuds = sco_etud.get_etud_info(etudid=etudid)
|
||||||
|
if not etuds:
|
||||||
|
raise ScoValueError("étudiant inexistant")
|
||||||
|
etud = etuds[0]
|
||||||
|
etud_archive_id = etud["scodoc7_id"] or etudid
|
||||||
L = []
|
L = []
|
||||||
for archive_id in EtudsArchive.list_obj_archives(etudid):
|
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||||
a = {
|
a = {
|
||||||
"archive_id": archive_id,
|
"archive_id": archive_id,
|
||||||
"description": EtudsArchive.get_archive_description(archive_id),
|
"description": EtudsArchive.get_archive_description(archive_id),
|
||||||
@ -113,7 +118,8 @@ def add_archives_info_to_etud_list(etuds):
|
|||||||
"""
|
"""
|
||||||
for etud in etuds:
|
for etud in etuds:
|
||||||
l = []
|
l = []
|
||||||
for archive_id in EtudsArchive.list_obj_archives(etud["etudid"]):
|
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||||
|
for archive_id in EtudsArchive.list_obj_archives(etud_archive_id):
|
||||||
l.append(
|
l.append(
|
||||||
"%s (%s)"
|
"%s (%s)"
|
||||||
% (
|
% (
|
||||||
@ -129,9 +135,12 @@ def etud_upload_file_form(REQUEST, etudid):
|
|||||||
# check permission
|
# check permission
|
||||||
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
if not can_edit_etud_archive(REQUEST.AUTHENTICATED_USER):
|
||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
"opération non autorisée pour %s" % REQUEST.AUTHENTICATED_USER
|
||||||
)
|
)
|
||||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
etuds = sco_etud.get_etud_info(filled=True)
|
||||||
|
if not etuds:
|
||||||
|
raise ScoValueError("étudiant inexistant")
|
||||||
|
etud = etuds[0]
|
||||||
H = [
|
H = [
|
||||||
html_sco_header.sco_header(
|
html_sco_header.sco_header(
|
||||||
page_title="Chargement d'un document associé à %(nomprenom)s" % etud,
|
page_title="Chargement d'un document associé à %(nomprenom)s" % etud,
|
||||||
@ -172,18 +181,21 @@ def etud_upload_file_form(REQUEST, etudid):
|
|||||||
data = tf[2]["datafile"].read()
|
data = tf[2]["datafile"].read()
|
||||||
descr = tf[2]["description"]
|
descr = tf[2]["description"]
|
||||||
filename = tf[2]["datafile"].filename
|
filename = tf[2]["datafile"].filename
|
||||||
_store_etud_file_to_new_archive(etudid, data, filename, description=descr)
|
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||||
|
_store_etud_file_to_new_archive(
|
||||||
|
etud_archive_id, data, filename, description=descr
|
||||||
|
)
|
||||||
return flask.redirect(
|
return flask.redirect(
|
||||||
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
url_for("scolar.ficheEtud", scodoc_dept=g.scodoc_dept, etudid=etudid)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def _store_etud_file_to_new_archive(etudid, data, filename, description=""):
|
def _store_etud_file_to_new_archive(etud_archive_id, data, filename, description=""):
|
||||||
"""Store data to new archive."""
|
"""Store data to new archive."""
|
||||||
filesize = len(data)
|
filesize = len(data)
|
||||||
if filesize < 10 or filesize > scu.CONFIG.ETUD_MAX_FILE_SIZE:
|
if filesize < 10 or filesize > scu.CONFIG.ETUD_MAX_FILE_SIZE:
|
||||||
return 0, "Fichier image de taille invalide ! (%d)" % filesize
|
return 0, "Fichier image de taille invalide ! (%d)" % filesize
|
||||||
archive_id = EtudsArchive.create_obj_archive(etudid, description)
|
archive_id = EtudsArchive.create_obj_archive(etud_archive_id, description)
|
||||||
EtudsArchive.store(archive_id, filename, data)
|
EtudsArchive.store(archive_id, filename, data)
|
||||||
|
|
||||||
|
|
||||||
@ -194,8 +206,12 @@ def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
|||||||
raise AccessDenied(
|
raise AccessDenied(
|
||||||
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
"opération non autorisée pour %s" % str(REQUEST.AUTHENTICATED_USER)
|
||||||
)
|
)
|
||||||
etud = sco_etud.get_etud_info(filled=True)[0]
|
etuds = sco_etud.get_etud_info(filled=True)
|
||||||
archive_id = EtudsArchive.get_id_from_name(etudid, archive_name)
|
if not etuds:
|
||||||
|
raise ScoValueError("étudiant inexistant")
|
||||||
|
etud = etuds[0]
|
||||||
|
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||||
|
archive_id = EtudsArchive.get_id_from_name(etud_archive_id, archive_name)
|
||||||
if not dialog_confirmed:
|
if not dialog_confirmed:
|
||||||
return scu.confirm_dialog(
|
return scu.confirm_dialog(
|
||||||
"""<h2>Confirmer la suppression des fichiers ?</h2>
|
"""<h2>Confirmer la suppression des fichiers ?</h2>
|
||||||
@ -228,7 +244,14 @@ def etud_delete_archive(REQUEST, etudid, archive_name, dialog_confirmed=False):
|
|||||||
|
|
||||||
def etud_get_archived_file(REQUEST, etudid, archive_name, filename):
|
def etud_get_archived_file(REQUEST, etudid, archive_name, filename):
|
||||||
"""Send file to client."""
|
"""Send file to client."""
|
||||||
return EtudsArchive.get_archived_file(REQUEST, etudid, archive_name, filename)
|
etuds = sco_etud.get_etud_info(filled=True)
|
||||||
|
if not etuds:
|
||||||
|
raise ScoValueError("étudiant inexistant")
|
||||||
|
etud = etuds[0]
|
||||||
|
etud_archive_id = etud["scodoc7_id"] or etud["etudid"]
|
||||||
|
return EtudsArchive.get_archived_file(
|
||||||
|
REQUEST, etud_archive_id, archive_name, filename
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants)
|
# --- Upload d'un ensemble de fichiers (pour un groupe d'étudiants)
|
||||||
@ -260,17 +283,22 @@ def etudarchive_import_files_form(group_id, REQUEST=None):
|
|||||||
page_title="Import de fichiers associés aux étudiants"
|
page_title="Import de fichiers associés aux étudiants"
|
||||||
),
|
),
|
||||||
"""<h2 class="formsemestre">Téléchargement de fichier associés aux étudiants</h2>
|
"""<h2 class="formsemestre">Téléchargement de fichier associés aux étudiants</h2>
|
||||||
<p>Les fichiers associés (dossiers d'admission, certificats, ...), de types quelconques (pdf, doc, images)
|
<p>Les fichiers associés (dossiers d'admission, certificats, ...), de
|
||||||
sont accessibles aux utilisateurs via la fiche individuelle de l'étudiant.
|
types quelconques (pdf, doc, images) sont accessibles aux utilisateurs via
|
||||||
|
la fiche individuelle de l'étudiant.
|
||||||
</p>
|
</p>
|
||||||
<p class="warning">Ne pas confondre avec les photos des étudiants, qui se chargent via l'onglet "Photos".</p>
|
<p class="warning">Ne pas confondre avec les photos des étudiants, qui se
|
||||||
<p><b>Vous pouvez aussi charger à tout moment de nouveaux fichiers, ou en supprimer, via la fiche de chaque étudiant.</b></p>
|
chargent via l'onglet "Photos".</p>
|
||||||
<p class="help">Cette page permet de charger en une seule fois les fichiers de plusieurs étudiants.<br/>
|
<p><b>Vous pouvez aussi charger à tout moment de nouveaux fichiers, ou en
|
||||||
|
supprimer, via la fiche de chaque étudiant.</b>
|
||||||
|
</p>
|
||||||
|
<p class="help">Cette page permet de charger en une seule fois les fichiers
|
||||||
|
de plusieurs étudiants.<br/>
|
||||||
Il faut d'abord remplir une feuille excel donnant les noms
|
Il faut d'abord remplir une feuille excel donnant les noms
|
||||||
des fichiers (un fichier par étudiant).
|
des fichiers (un fichier par étudiant).
|
||||||
</p>
|
</p>
|
||||||
<p class="help">Ensuite, réunir vos fichiers dans un fichier zip, puis télécharger
|
<p class="help">Ensuite, réunir vos fichiers dans un fichier zip, puis
|
||||||
simultanément le fichier excel et le fichier zip.
|
télécharger simultanément le fichier excel et le fichier zip.
|
||||||
</p>
|
</p>
|
||||||
<ol>
|
<ol>
|
||||||
<li><a class="stdlink" href="etudarchive_generate_excel_sample?group_id=%s">
|
<li><a class="stdlink" href="etudarchive_generate_excel_sample?group_id=%s">
|
||||||
|
@ -256,6 +256,7 @@ _identiteEditor = ndb.EditableTable(
|
|||||||
"photo_filename",
|
"photo_filename",
|
||||||
"code_ine",
|
"code_ine",
|
||||||
"code_nip",
|
"code_nip",
|
||||||
|
"scodoc7_id",
|
||||||
),
|
),
|
||||||
filter_dept=True,
|
filter_dept=True,
|
||||||
sortkey="nom",
|
sortkey="nom",
|
||||||
@ -655,9 +656,9 @@ def make_etud_args(etudid=None, code_nip=None, use_request=True, raise_exc=True)
|
|||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
def get_etud_info(etudid=False, code_nip=False, filled=False):
|
def get_etud_info(etudid=False, code_nip=False, filled=False) -> list:
|
||||||
"""infos sur un etudiant (API)
|
"""infos sur un etudiant (API). If not foud, returns empty list.
|
||||||
On peut specifier etudid ou conde_nip
|
On peut specifier etudid ou code_nip
|
||||||
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
|
ou bien cherche dans REQUEST.form: etudid, code_nip, code_ine
|
||||||
(dans cet ordre).
|
(dans cet ordre).
|
||||||
"""
|
"""
|
||||||
|
@ -67,6 +67,7 @@ _formsemestreEditor = ndb.EditableTable(
|
|||||||
"ens_can_edit_eval",
|
"ens_can_edit_eval",
|
||||||
"elt_sem_apo",
|
"elt_sem_apo",
|
||||||
"elt_annee_apo",
|
"elt_annee_apo",
|
||||||
|
"scodoc7_id",
|
||||||
),
|
),
|
||||||
filter_dept=True,
|
filter_dept=True,
|
||||||
sortkey="date_debut",
|
sortkey="date_debut",
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
"""ScoDoc 9.0.4: ajout id scodoc7 pour migrations (archives)
|
||||||
|
|
||||||
|
Revision ID: 017e32eb4773
|
||||||
|
Revises: 6b071b7947e5
|
||||||
|
Create Date: 2021-08-27 21:58:05.317092
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '017e32eb4773'
|
||||||
|
down_revision = '6b071b7947e5'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.add_column('identite', sa.Column('scodoc7_id', sa.Text(), nullable=True))
|
||||||
|
op.add_column('notes_formsemestre', sa.Column('scodoc7_id', sa.Text(), nullable=True))
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_column('notes_formsemestre', 'scodoc7_id')
|
||||||
|
op.drop_column('identite', 'scodoc7_id')
|
||||||
|
# ### end Alembic commands ###
|
@ -260,6 +260,8 @@ def convert_object(
|
|||||||
if id_name:
|
if id_name:
|
||||||
old_id = obj[id_name]
|
old_id = obj[id_name]
|
||||||
del obj[id_name]
|
del obj[id_name]
|
||||||
|
if hasattr(klass, "scodoc7_id"):
|
||||||
|
obj["scodoc7_id"] = old_id
|
||||||
else:
|
else:
|
||||||
old_id = None # tables ScoDoc7 sans id
|
old_id = None # tables ScoDoc7 sans id
|
||||||
if is_table:
|
if is_table:
|
||||||
@ -283,6 +285,7 @@ def convert_object(
|
|||||||
if (k.endswith("id") or k == "object") and k not in USER_REFS | {
|
if (k.endswith("id") or k == "object") and k not in USER_REFS | {
|
||||||
"semestre_id",
|
"semestre_id",
|
||||||
"sem_id",
|
"sem_id",
|
||||||
|
"scodoc7_id",
|
||||||
}:
|
}:
|
||||||
old_ref = obj[k]
|
old_ref = obj[k]
|
||||||
if old_ref is not None:
|
if old_ref is not None:
|
||||||
|
Loading…
Reference in New Issue
Block a user