registration, charts, contracts history
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import os
|
||||
import hashlib
|
||||
import time
|
||||
import ssl
|
||||
@@ -20,6 +21,8 @@ from email.mime.application import MIMEApplication
|
||||
import pdfkit
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
import jwt
|
||||
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from . model import db, UploadRegular, UploadSolar, RegisteredUser, UserInstitutionMapping, Institution, InstitutionContract, CorpusAccess
|
||||
@@ -238,8 +241,11 @@ class UploadHandler:
|
||||
return None
|
||||
|
||||
|
||||
def get_user_institutions(user_id):
|
||||
return UserInstitutionMapping.query.filter_by(user=user_id).all()
|
||||
def get_user_institution(user_id):
|
||||
mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
|
||||
if mapping:
|
||||
return Institution.query.filter_by(id=mapping.institution).first()
|
||||
return None
|
||||
|
||||
|
||||
def has_user_corpus_access(user_id, corpus_name):
|
||||
@@ -252,13 +258,11 @@ def has_user_corpus_access(user_id, corpus_name):
|
||||
return True
|
||||
|
||||
# Check if user belongs to an institution, that has access to this corpus.
|
||||
institutions = get_user_institutions(user_id)
|
||||
institution = get_user_institution(user_id)
|
||||
has_access = False
|
||||
for institution in institutions:
|
||||
row = CorpusAccess.query.filter_by(institution=institution.id, corpus=corpus_name).first()
|
||||
if row:
|
||||
has_access = True
|
||||
break
|
||||
row = CorpusAccess.query.filter_by(institution=institution.id, corpus=corpus_name).first()
|
||||
if row:
|
||||
has_access = True
|
||||
return has_access
|
||||
|
||||
|
||||
@@ -322,15 +326,86 @@ def add_user_to_institution(user_id, institution_id, role):
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def activate_user(user_id):
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'active': True})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def update_user_password(user_id, new_password):
|
||||
phash = generate_password_hash(new_password)
|
||||
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'pass_hash': pass_hash})
|
||||
db.session.commit()
|
||||
return rowcount
|
||||
|
||||
|
||||
def del_user_from_institution(user_id, institution_id):
|
||||
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.institution == institution_id).filter(UserInstitutionMapping.user == user_id).delete()
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
return RegisteredUser.query.filter_by(active=True).all()
|
||||
return RegisteredUser.query.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()
|
||||
|
||||
def get_all_active_users_join_institutions():
|
||||
#return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
|
||||
return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).order_by(RegisteredUser.id).all()
|
||||
|
||||
def get_all_active_institution_users(institution_id):
|
||||
return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping,
|
||||
RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all()
|
||||
|
||||
|
||||
def is_institution_moderator(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
if user_inst_mapping.role != 'moderator':
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def is_institution_member(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def get_password_reset_token(email, expires=500):
|
||||
return jwt.encode({'reset_password': email,
|
||||
'exp': time() + expires},
|
||||
key=os.getenv('APP_SECRET_KEY'), algorithm='HS256')
|
||||
|
||||
def verify_reset_token(token):
|
||||
try:
|
||||
email = jwt.decode(token,
|
||||
key=os.getenv('APP_SECRET_KEY'), algorithms=["HS256"])['reset_password']
|
||||
except Exception as e:
|
||||
logging.error(e)
|
||||
return
|
||||
return RegisteredUser.query.filter_by(email=email).first()
|
||||
|
||||
|
||||
def send_resetpass_mail(email, config):
|
||||
jwt_token = get_password_reset_token(email)
|
||||
|
||||
text = '''
|
||||
Zahtevali ste ponastavitev gesla vašega uporabniškega računa.
|
||||
|
||||
Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format(
|
||||
'https://zbiranje.slovenscina.eu/solar/resetpass/{}'.format(jwt_token))
|
||||
|
||||
# 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'], email, text)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
import logging
|
||||
import traceback
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
import portal.base
|
||||
from portal.base import UploadHandler, ContractCreator, REGEX_EMAIL
|
||||
from portal.model import db, UploadPredavanja
|
||||
|
||||
|
||||
MAXLEN_FORM = 150
|
||||
|
||||
class UploadHandlerPredavanja(UploadHandler):
|
||||
|
||||
ENABLED_FILETYPES = None # None means all filetypes
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.contract_creator = ContractCreator(base_path=self.get_uploads_subdir('contracts'),
|
||||
template_path='contract/predavanja.html')
|
||||
|
||||
def generate_upload_contract_pdf(self, upload_metadata):
|
||||
form_data = upload_metadata['form_data']
|
||||
|
||||
files_table_str = []
|
||||
for file_name in upload_metadata['file_names']:
|
||||
files_table_str.append('<tr><td style="text-align: center;">')
|
||||
files_table_str.append(file_name)
|
||||
files_table_str.append('</td></tr>')
|
||||
files_table_str = ''.join(files_table_str)
|
||||
|
||||
data = {
|
||||
'ime_priimek': form_data['ime'],
|
||||
'files_table_str': files_table_str
|
||||
}
|
||||
|
||||
self.contract_creator.create_pdf(upload_metadata['contract_file'], data)
|
||||
|
||||
@staticmethod
|
||||
def store_metadata(upload_metadata):
|
||||
timestamp = datetime.fromtimestamp(upload_metadata['timestamp'])
|
||||
form_data = upload_metadata['form_data']
|
||||
file_hashes = upload_metadata['file_hashes_dict']
|
||||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
|
||||
# Normalize keywords list
|
||||
keywords_list = []
|
||||
for keyword in form_data['kljucne-besede'].split(','):
|
||||
keyword = keyword.strip()
|
||||
keywords_list.append(keyword)
|
||||
keywords = ','.join(keywords_list)
|
||||
|
||||
try:
|
||||
model_obj = UploadPredavanja(
|
||||
upload_hash=upload_metadata['upload_id'],
|
||||
timestamp=timestamp,
|
||||
name=form_data['ime'],
|
||||
address=form_data['naslov-predavanja'],
|
||||
subject=form_data['predmet'],
|
||||
faculty=form_data['fakulteta'],
|
||||
email=form_data['email'],
|
||||
phone=form_data.get('phone'),
|
||||
keywords=keywords,
|
||||
agree_publish_future=form_data['javna-objava-prihodnost'],
|
||||
agree_machine_translation=True if 'strojno-prevajanje' in form_data else False,
|
||||
agree_news_cjvt=True if 'obvestila' in form_data else False,
|
||||
file_contract=upload_metadata['contract_file'],
|
||||
upload_file_hashes=sorted_f_hashes,
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
def handle_upload(self, request):
|
||||
err = self.check_upload_request(request)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
err = self.check_form(request.form)
|
||||
if err:
|
||||
return err, 400
|
||||
|
||||
# Parse request.
|
||||
upload_metadata = self.extract_upload_metadata('predavanja', request)
|
||||
|
||||
logging.info('Upload for "predavanja" with id "{}" supplied form data: {}'.format(
|
||||
upload_metadata['upload_id'], str(upload_metadata['form_data'])))
|
||||
|
||||
# Store uploaded files to disk.
|
||||
self.store_datafiles(request.files, upload_metadata)
|
||||
|
||||
# Store metadata to database.
|
||||
self.store_metadata(upload_metadata)
|
||||
|
||||
# Send confirmation mail
|
||||
self.send_confirm_mail(upload_metadata)
|
||||
|
||||
return 'Uspešno ste oddali datotek(e). Št. datotek: {}'.format(len(request.files))
|
||||
|
||||
|
||||
@staticmethod
|
||||
def check_form(form):
|
||||
name = form.get('ime')
|
||||
address = form.get('naslov-predavanja')
|
||||
subject = form.get('predmet')
|
||||
faculty = form.get('fakulteta')
|
||||
email = form.get('email')
|
||||
phone = form.get('telefon')
|
||||
keywords = form.get('kljucne-besede')
|
||||
agree_publish_future = form.get('javna-objava-prihodnost')
|
||||
|
||||
if not agree_publish_future:
|
||||
return 'Manjkajoča izbrana vrednost pri polju za javno objavo.'
|
||||
|
||||
if not name \
|
||||
or not address \
|
||||
or not subject \
|
||||
or not faculty \
|
||||
or not email \
|
||||
or not keywords:
|
||||
return 'Izpolnite vsa obvezna polja.'
|
||||
|
||||
#for keyword in keywords.split(','):
|
||||
# keyword = keyword.strip()
|
||||
# if keyword.isspace() or not keyword.replace(' ', '').isalpha():
|
||||
# return 'Ključna beseda "{}" ni pravilnega formata.'.format(keyword)
|
||||
|
||||
if not re.search(REGEX_EMAIL, email):
|
||||
return 'Email napačnega formata.'
|
||||
|
||||
for key, val in form.items():
|
||||
if key == 'kljucne-besde':
|
||||
if len(val) > 500:
|
||||
return 'Polje "{}" presega dolžino {} znakov.'.format(key, 500)
|
||||
else:
|
||||
if len(val) > MAXLEN_FORM:
|
||||
return 'Polje "{}" presega dolžino {} znakov.'.format(key, MAXLEN_FORM)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import logging
|
||||
import re
|
||||
import traceback
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from sqlalchemy import desc, exists
|
||||
from sqlalchemy import desc
|
||||
from collections import Counter
|
||||
|
||||
from portal.base import UploadHandler, get_user_institutions, has_user_corpus_access
|
||||
from portal.model import db, UploadSolar, ContractsSolar, RegisteredUser, Institution, InstitutionContract, UserInstitutionMapping, CorpusAccess
|
||||
from portal.base import UploadHandler, get_user_institution, has_user_corpus_access
|
||||
from portal.model import UploadSolar, ContractsSolar, RegisteredUser, Institution, InstitutionContract, UserInstitutionMapping, CorpusAccess
|
||||
|
||||
|
||||
VALID_PROGRAMS = {'OS', 'SSG', 'MGP', 'ZG', 'NPI', 'SPI', 'SSI', 'PTI'}
|
||||
@@ -27,8 +27,7 @@ class UploadHandlerSolar(UploadHandler):
|
||||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
|
||||
# If user is mapped to multiple institutions, let him chose in name of which one he makes the upload.
|
||||
institution_id = get_user_institutions(user_id)[0].id
|
||||
institution_id = get_user_institution(user_id).id
|
||||
|
||||
model_obj = UploadSolar(
|
||||
upload_user = user_id,
|
||||
@@ -147,7 +146,7 @@ class UploadHandlerSolar(UploadHandler):
|
||||
if program not in VALID_PROGRAMS:
|
||||
return 'Invalid program "{}"'.format(program)
|
||||
if predmet not in VALID_SUBJECTS:
|
||||
return 'Invalid subject "{}"'.format(premdet)
|
||||
return 'Invalid subject "{}"'.format(predmet)
|
||||
if letnik < 1 or letnik > 9:
|
||||
return 'Invalid grade: {}'.format(letnik)
|
||||
if vrsta not in VALID_TEXT_TYPES:
|
||||
@@ -176,14 +175,30 @@ def get_all_institutions():
|
||||
return res
|
||||
|
||||
|
||||
def get_institution_student_contracts(institution_id):
|
||||
return ContractsSolar.query.filter_by(institution=institution_id, contract_type='ucenci-starsi').all()
|
||||
def get_institution_student_contracts(institution_id, user_id=None):
|
||||
if not user_id:
|
||||
return ContractsSolar.query.filter_by(institution=institution_id, contract_type='ucenci-starsi').all()
|
||||
return ContractsSolar.query.filter_by(institution=institution_id, contract_type='ucenci-starsi', upload_user=user_id).all()
|
||||
|
||||
|
||||
def get_institution_contract(institution_id):
|
||||
return InstitutionContract.query.filter_by(institution=institution_id, corpus='solar').order_by(desc(InstitutionContract.timestamp)).first()
|
||||
|
||||
|
||||
def get_top_uploading_institutions():
|
||||
res = dict()
|
||||
institutions = get_all_institutions()
|
||||
for institution in institutions:
|
||||
uploads = UploadSolar.query.filter_by(institution=institution.id).all()
|
||||
for upload in uploads:
|
||||
if institution.name not in res:
|
||||
res[institution.name] = 0
|
||||
res[institution.name] += len(upload.upload_file_hashes)
|
||||
if len(res) >= 5:
|
||||
return dict(sorted(res.items(), key=lambda x:x[1], reverse=True)[:5])
|
||||
return dict(sorted(res.items(), key=lambda x:x[1], reverse=True))
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
# TODO: do filtering purely within an SQL query
|
||||
res = []
|
||||
|
||||
Reference in New Issue
Block a user