Šolar update.

This commit is contained in:
msinkec
2021-09-16 17:25:20 +02:00
parent 788f3bc3eb
commit d60ef26741
9 changed files with 595 additions and 243 deletions

View File

@@ -68,7 +68,7 @@ class ContractCreator:
class UploadHandler:
ENABLED_FILETYPES = ['txt', 'csv', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'xml', 'mxliff', 'tmx']
ENABLED_FILETYPES = ['txt', 'csv', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'xml', 'mxliff', 'tmx', 'jpg', 'jpeg', 'png']
def __init__(self, **kwargs):
self.config = kwargs
@@ -210,7 +210,7 @@ class UploadHandler:
for key, f in files.items():
if key.startswith('file'):
suffix = f.filename.split('.')[-1]
if self.ENABLED_FILETYPES and suffix not in self.ENABLED_FILETYPES:
if self.ENABLED_FILETYPES and suffix.lower() not in self.ENABLED_FILETYPES:
return 'Datoteka "{}" ni pravilnega formata.'.format(f.filename)
return None
@@ -263,10 +263,10 @@ def get_institution_cooperation_history(institution_id):
if row.user not in res:
res[row.user] = {
'coordinator': [],
'moderator': [],
'mentor': [],
'other': []
}
res[row.user][row.role].append(row.school_year)
res[row.user][row.role].append((row.school_year, row.badge_text))
return res
@@ -275,6 +275,22 @@ def get_cooperation_history():
return UserCooperationHistory.query.all()
def add_cooperation_history_item(user_id, institution_id, role, school_year, badge_text):
model_obj = UserCooperationHistory(
user=user_id,
institution=institution_id,
role=role,
school_year=school_year,
badge_text=badge_text
)
db.session.add(model_obj)
db.session.commit()
return model_obj.id
def del_cooperation_history_item(entry_id):
db.session.query(UserCooperationHistory).filter_by(id=entry_id).delete()
db.session.commit()
def has_user_corpus_access(user_id, corpus_name):
user = RegisteredUser.query.filter_by(id=user_id).first()
@@ -303,6 +319,8 @@ def is_admin(user_id):
def get_user_obj(user_id):
return RegisteredUser.query.filter_by(id=user_id).first()
def get_user_obj_by_email(email):
return RegisteredUser.query.filter_by(email=email).first()
def get_institution_obj(institution_id):
return Institution.query.filter_by(id=institution_id).first()
@@ -370,10 +388,38 @@ def update_user_password(user_id, new_password):
return rowcount
def del_user(user_id):
db.session.query(UserCooperationHistory).filter(UserCooperationHistory.user == user_id).delete()
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.user == user_id).delete()
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).delete()
def update_user_role(user_id, role):
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'role': role})
db.session.commit()
return rowcount
def update_user_email(user_id, new_email):
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'email': new_email})
db.session.commit()
return rowcount
def update_user_name(user_id, new_name):
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'name': new_name})
db.session.commit()
return rowcount
def remove_user(user_id):
#db.session.query(UserCooperationHistory).filter(UserCooperationHistory.user == user_id).delete()
#db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.user == user_id).delete()
#db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).delete()
#db.session.commit()
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': True})
db.session.commit()
def undo_remove_user(user_id):
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).update({'is_removed': False})
db.session.commit()
def remove_institution(institution_id):
db.session.query(Institution).filter(Institution.id == institution_id).update({'is_removed': True})
db.session.commit()
@@ -383,22 +429,22 @@ def del_user_from_institution(user_id, institution_id):
def get_all_active_users():
return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
return RegisteredUser.query.filter_by(is_removed=False).filter_by(active=True).order_by(RegisteredUser.id).all()
def get_all_inactive_users():
return RegisteredUser.query.filter_by(active=False).order_by(RegisteredUser.id).all()
return RegisteredUser.query.filter_by(is_removed=False).filter_by(active=False).order_by(RegisteredUser.id).all()
def get_all_users_join_institutions(active=True):
#return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping,
return db.session.query(RegisteredUser, UserInstitutionMapping).filter(RegisteredUser.is_removed==False).outerjoin(UserInstitutionMapping,
RegisteredUser.id == UserInstitutionMapping.user).filter(RegisteredUser.active == active).order_by(RegisteredUser.id).all()
def get_all_active_institution_users(institution_id):
return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping,
return RegisteredUser.query.filter_by(is_removed=False).filter_by(active=True).join(UserInstitutionMapping,
RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all()
@@ -437,6 +483,28 @@ def get_password_reset_token(email, key, expires=600):
'exp': int(time.time()) + expires},
key=key, algorithm='HS256')
def transfer_users_institution(institution_id_from, institution_id_to):
rowcount = db.session.query(UserInstitutionMapping).filter_by(institution=institution_id_from).update(
{'institution': institution_id_to})
db.session.commit()
return rowcount
def transfer_uploads_institution(institution_id_from, institution_id_to):
rowcount = db.session.query(UploadSolar).filter_by(institution=institution_id_from).update(
{'institution': institution_id_to})
db.session.commit()
return rowcount
def transfer_contracts_institution(institution_id_from, institution_id_to):
rowcount = db.session.query(ContractsSolar).filter_by(institution=institution_id_from).update(
{'institution': institution_id_to})
db.session.commit()
return rowcount
def verify_reset_token(token, key):
try:
message = jwt.decode(token,
@@ -455,11 +523,18 @@ def verify_reset_token(token, key):
def send_resetpass_mail(email, config):
jwt_token = get_password_reset_token(email, config['APP_SECRET_KEY'])
text = '''
body = '''
Zahtevali ste ponastavitev gesla vašega uporabniškega računa.
Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format(jwt_token)
message = MIMEMultipart()
message['From'] = config['MAIL_LOGIN']
message['To'] = email
message['Subject'] = 'Ponastavitev gesla'
message.attach(MIMEText(body, "plain"))
text = message.as_string()
# Create a secure SSL context
context = ssl.create_default_context()
@@ -469,3 +544,53 @@ def send_resetpass_mail(email, config):
server.sendmail(config['MAIL_LOGIN'], email, text)
except Exception:
traceback.print_exc()
def send_admins_new_user_notification_mail(user_id, config):
user = RegisteredUser.query.filter_by(id=user_id).first()
body = '''
Nov uporabnik "{}" je ustvaril uporabniški račun na portalu za oddajanje besedil Šolar in čaka na odobritev.
'''.format(user.name)
message = MIMEMultipart()
message['From'] = config['MAIL_LOGIN']
message['To'] = email
message['Subject'] = 'Ponastavitev gesla'
message.attach(MIMEText(body, "plain"))
text = message.as_string()
admins = RegisteredUser.query.filter_by(role="admin").all()
# Create a secure SSL context
context = ssl.create_default_context()
for admin in admins:
try:
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
server.sendmail(config['MAIL_LOGIN'], admin.email, text)
except Exception:
traceback.print_exc()
def send_user_activation_mail(user_id, config):
user = RegisteredUser.query.filter_by(id=user_id).first()
body = '''Vaš uporabniški račun "{}" na portalu Šolar je bil odobren.'''.format(user.name)
message = MIMEMultipart()
message['From'] = config['MAIL_LOGIN']
message['To'] = email
message['Subject'] = 'Ponastavitev gesla'
message.attach(MIMEText(body, "plain"))
text = message.as_string()
# Create a secure SSL context
context = ssl.create_default_context()
try:
with SMTP_SSL(config['MAIL_HOST'], config['SMTP_PORT'], context=context) as server:
server.login(config['MAIL_LOGIN'], config['MAIL_PASS'])
server.sendmail(config['MAIL_LOGIN'], user.email, text)
except Exception:
traceback.print_exc()

View File

@@ -67,6 +67,7 @@ class RegisteredUser(UserMixin, db.Model):
active = db.Column(db.Boolean, nullable=True)
last_login = db.Column(db.DateTime, nullable=True)
registered = db.Column(db.DateTime, nullable=True)
is_removed = db.Column(db.Boolean, nullable=False, default=False, server_default="false")
class UserInstitutionMapping(db.Model):
@@ -104,6 +105,7 @@ class Institution(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False, unique=True)
region = db.Column(db.String, nullable=False)
is_removed = db.Column(db.Boolean, nullable=False, default=False, server_default="false")
class CorpusAccess(db.Model):

View File

@@ -79,62 +79,58 @@ class UploadHandlerSolar(UploadHandler):
if contract_type not in ['sola', 'ucenci-starsi']:
return 'Neveljaven tip pogodbe.'
f_obj = None
for key, f in request.files.items():
if key.startswith('file'):
mimetype = f.content_type
if mimetype != 'application/pdf':
return 'Datoteka "{}" ni formata PDF.'.format(f.filename)
else:
f_obj = f
break
if not f_obj:
return 'Niste naložili nobene datoteke.'
#for key, f in request.files.items():
for f in request.files.getlist("file[]"):
base = self.get_uploads_subdir('contracts')
f_hash = hashlib.md5(f_obj.read()).hexdigest()
f_obj.seek(0, 0)
mimetype = f.content_type
if mimetype != 'application/pdf':
return 'Datoteka "{}" ni formata PDF.'.format(f.filename)
if not f:
return 'Niste naložili nobene datoteke.'
# First byte used for indexing, similarly like git does for example.
sub_dir = base / f_hash[:2]
if not sub_dir.exists():
sub_dir.mkdir()
base = self.get_uploads_subdir('contracts')
f_hash = hashlib.md5(f.read()).hexdigest()
f.seek(0, 0)
path = sub_dir / (f_hash[2:] + '.pdf')
f_obj.save(path)
# First byte used for indexing, similarly like git does for example.
sub_dir = base / f_hash[:2]
if not sub_dir.exists():
sub_dir.mkdir()
timestamp = datetime.now()
user_obj = RegisteredUser.query.filter_by(id=user_id).one()
user_institution_mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
if user_institution_mapping is None:
return 'Vaš uporabnik ni dodeljen nobeni inštituciji.'
institution_id = user_institution_mapping.institution
is_institution_coordinator = True if user_institution_mapping.role == 'coordinator' else False
path = sub_dir / (f_hash[2:] + '.pdf')
f.save(path)
if contract_type == 'sola':
if not is_institution_coordinator:
return 'Vaš uporabnik nima pravic za nalaganje pogodbe s šolo.'
# TODO: insert institution contract
model_obj = InstitutionContract(
institution=institution_id,
corpus='solar',
timestamp=timestamp,
file_contract=f_hash,
original_filename=f_obj.filename
)
self.store_model(model_obj)
else:
model_obj = ContractsSolar(
institution=institution_id,
upload_user=user_id,
timestamp=timestamp,
file_contract=f_hash,
contract_type=contract_type,
original_filename=f_obj.filename
)
timestamp = datetime.now()
user_institution_mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
if user_institution_mapping is None:
return 'Vaš uporabnik ni dodeljen nobeni inštituciji.'
institution_id = user_institution_mapping.institution
is_institution_coordinator = True if user_institution_mapping.role == 'coordinator' else False
self.store_model(model_obj)
if contract_type == 'sola':
if not is_institution_coordinator:
return 'Vaš uporabnik nima pravic za nalaganje pogodbe s šolo.'
# TODO: insert institution contract
model_obj = InstitutionContract(
institution=institution_id,
corpus='solar',
timestamp=timestamp,
file_contract=f_hash,
original_filename=f.filename
)
self.store_model(model_obj)
else:
model_obj = ContractsSolar(
institution=institution_id,
upload_user=user_id,
timestamp=timestamp,
file_contract=f_hash,
contract_type=contract_type,
original_filename=f.filename
)
self.store_model(model_obj)
return 'Nalaganje pogodbe je bilo uspešno.'
@staticmethod
@@ -168,10 +164,14 @@ def get_upload_history(user_id, n=20):
return UploadSolar.query.filter_by(upload_user=user_id).order_by(desc(UploadSolar.timestamp)).limit(n).all()
def get_institution_upload_history(institution_id, n=20):
return UploadSolar.query.filter_by(institution=institution_id).order_by(desc(UploadSolar.timestamp)).limit(n).all()
def get_all_institutions():
# TODO: do filtering purely within an SQL query
res = []
for institution in Institution.query.all():
for institution in Institution.query.filter_by(is_removed=False).all():
row = CorpusAccess.query.filter_by(institution=institution.id, corpus='solar').first()
if row:
res.append(institution)