solar update
This commit is contained in:
parent
31ce97cb44
commit
6697c60c7f
169
app.py
169
app.py
|
@ -9,7 +9,7 @@ from flask import Flask, render_template, request, redirect, flash, safe_join, s
|
|||
from flask_dropzone import Dropzone
|
||||
from flask_migrate import Migrate, MigrateCommand
|
||||
from flask_script import Manager
|
||||
from flask_login import LoginManager, login_required, login_user, current_user
|
||||
from flask_login import LoginManager, login_required, login_user, current_user, logout_user
|
||||
from portal.model import db, RegisteredUser
|
||||
|
||||
import portal.base
|
||||
|
@ -193,7 +193,8 @@ def index_corpus(corpus_name):
|
|||
|
||||
@login_manager.user_loader
|
||||
def load_user(user_id):
|
||||
return RegisteredUser.query.get(int(user_id))
|
||||
user = RegisteredUser.query.get(int(user_id))
|
||||
return user
|
||||
|
||||
|
||||
@app.route('/solar/login')
|
||||
|
@ -204,7 +205,7 @@ def login_get():
|
|||
@app.route('/<corpus_name>/login', methods=['POST'])
|
||||
def login_post(corpus_name):
|
||||
if corpus_name not in ENABLED_CORPUSES or corpus_name not in CORPUSES_LOGIN_REQUIRED:
|
||||
return 404
|
||||
return '', 404
|
||||
|
||||
email = request.form.get('email')
|
||||
password = request.form.get('password')
|
||||
|
@ -222,23 +223,32 @@ def login_post(corpus_name):
|
|||
|
||||
# Check if user is authorized to log into this corpus. Admins are an exception.
|
||||
if not portal.base.has_user_corpus_access(user.id, corpus_name):
|
||||
flash('Nimate dostop do tega korpusa.')
|
||||
flash('Nimate dostopa do tega korpusa.')
|
||||
return redirect('/{}/login'.format(corpus_name))
|
||||
|
||||
#portal.base.add_user_session(user.id)
|
||||
login_user(user, remember=remember)
|
||||
|
||||
if corpus_name == 'solar':
|
||||
return redirect('/solar/oddaja')
|
||||
return 404
|
||||
return '', 404
|
||||
|
||||
|
||||
# TODO: Move solar stuff to seperate file using Flask blueprints.
|
||||
# TODO: Better routing logic.
|
||||
|
||||
@app.route('/solar/logout')
|
||||
@login_required
|
||||
def logout():
|
||||
logout_user()
|
||||
return redirect('/solar/login')
|
||||
|
||||
|
||||
@app.route('/solar/<path:text>')
|
||||
@login_required
|
||||
def solar(text):
|
||||
if not portal.base.has_user_corpus_access(current_user.id, 'solar'):
|
||||
return 404
|
||||
return '', 404
|
||||
if text.startswith('oddaja/') or text == 'oddaja':
|
||||
return render_template('solar-oddaja.html')
|
||||
elif text.startswith('zgodovina/') or text == 'zgodovina':
|
||||
|
@ -256,51 +266,162 @@ def solar(text):
|
|||
institution_names=institution_names)
|
||||
elif text.startswith('pogodbe/') or text == 'pogodbe':
|
||||
# Check for ownload contract request.
|
||||
match = re.match('^pogodbe/[a-z0-9_]+\.pdf$', text)
|
||||
match = re.match('^pogodbe/([a-z0-9_]+\.pdf)$', text)
|
||||
if match:
|
||||
filename = match.group(1)
|
||||
safe_path = safe_join(str(upload_handler_solar.get_uploads_subdir('contracts')), filename)
|
||||
if len(filename) < 10:
|
||||
return '', 404
|
||||
prefix = filename[:2]
|
||||
suffix = filename[2:]
|
||||
|
||||
safe_path = safe_join(str(upload_handler_solar.get_uploads_subdir('contracts')), prefix, suffix)
|
||||
try:
|
||||
return send_file(safe_path, as_attachment=True)
|
||||
except FileNotFoundError:
|
||||
return 404
|
||||
return '', 404
|
||||
|
||||
user_obj = portal.base.get_user_obj(current_user.get_id())
|
||||
institution_id = user_obj.institution
|
||||
|
||||
institutions = portal.base.get_user_institutions(user_obj.id)
|
||||
contracts_students = []
|
||||
contract_school = None
|
||||
show_upload_form = True
|
||||
if institution_id:
|
||||
contracts_students = portal.solar.get_institution_student_contracts(institution_id)
|
||||
contract_school = portal.solar.get_institution_contract(institution_id)
|
||||
else:
|
||||
contract_school = []
|
||||
enable_upload_school_contract = False
|
||||
show_upload_form = False
|
||||
if len(institutions) > 0:
|
||||
show_upload_form = True
|
||||
institution = portal.base.get_user_institutions(user_obj.id)[0]
|
||||
contracts_students = portal.solar.get_institution_student_contracts(institution.id)
|
||||
contract_school = portal.solar.get_institution_contract(institution.id)
|
||||
|
||||
if portal.base.is_institution_moderator(user_obj.id, institution.id):
|
||||
enable_upload_school_contract = True
|
||||
|
||||
return render_template('solar-pogodbe.html', contracts_students=contracts_students,
|
||||
contract_school=contract_school, show_upload_form=show_upload_form)
|
||||
contract_school=contract_school,
|
||||
enable_upload_school_contract=enable_upload_school_contract,
|
||||
show_upload_form=show_upload_form)
|
||||
elif text.startswith('admin/') or text == 'admin':
|
||||
solar_users = portal.base.get_all_active_users()
|
||||
solar_institutions = portal.solar.get_all_institutions()
|
||||
if current_user.role == 'admin':
|
||||
return render_template('solar-admin.html')
|
||||
return 404
|
||||
return render_template('solar-admin.html', users=solar_users, institutions=solar_institutions)
|
||||
return '', 404
|
||||
|
||||
@app.route('/solar/pogodbe', methods=['POST'])
|
||||
@login_required
|
||||
def solar_upload_contract():
|
||||
if not portal.base.has_user_corpus_access(current_user.id, 'solar'):
|
||||
return 404
|
||||
return '', 404
|
||||
|
||||
return upload_handler_solar.handle_contract_upload(request, current_user.get_id())
|
||||
|
||||
|
||||
@app.route('/<corpus_name>/adduser', methods=['POST'])
|
||||
@login_required
|
||||
def solar_add_user(corpus_name):
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
return '', 404
|
||||
if not corpus_name in ENABLED_CORPUSES:
|
||||
return '', 404
|
||||
|
||||
name = request.form['name']
|
||||
email = request.form['email']
|
||||
password = request.form['password']
|
||||
|
||||
if not name:
|
||||
return 'Prazno polje za ime.'
|
||||
if len(name) > 100:
|
||||
return 'Predolgo ime.'
|
||||
|
||||
if not email:
|
||||
return 'Prazno polje za elektronsko pošto.'
|
||||
if len(email) > 100:
|
||||
return 'Predolgi email naslov'
|
||||
elif not re.search(portal.base.REGEX_EMAIL, email):
|
||||
return 'Email napačnega formata.'
|
||||
|
||||
if not password:
|
||||
return 'Prazno polje za geslo.'
|
||||
if len(password) > 100:
|
||||
return 'Predolgo geslo.'
|
||||
|
||||
portal.base.register_new_user(name, email, password)
|
||||
|
||||
return 'Uporabnik je bil dodan.'
|
||||
|
||||
|
||||
@app.route('/solar/deluser', methods=['POST'])
|
||||
@login_required
|
||||
def solar_del_user():
|
||||
# TODO: check if user is institution moderator for the added users institution or is an admin
|
||||
# TODO: delete from "user", "user_institution_mapping", update "institution_contract" set user to NULL
|
||||
return '', 404
|
||||
|
||||
@app.route('/<corpus_name>/addinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def add_institution(corpus_name):
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
return '', 404
|
||||
if not corpus_name in ENABLED_CORPUSES:
|
||||
return '', 404
|
||||
|
||||
name = request.form['name']
|
||||
region = request.form['region']
|
||||
|
||||
if not name:
|
||||
return 'Prazno polje za ime.'
|
||||
if len(name) > 100:
|
||||
return 'Predolgo ime.'
|
||||
|
||||
if not region:
|
||||
return 'Prazno polje za regijo.'
|
||||
if len(region) > 100:
|
||||
return 'Predolgi niz za regijo.'
|
||||
|
||||
institution_id = portal.base.add_institution(name, region)
|
||||
portal.base.grant_institution_corpus_access(institution_id, corpus_name)
|
||||
return 'Institucija je bila dodana.'
|
||||
|
||||
@app.route('/<corpus_name>/addusertoinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def add_user_institution_mapping(corpus_name):
|
||||
if not portal.base.is_admin(current_user.id):
|
||||
return '', 404
|
||||
if not corpus_name in ENABLED_CORPUSES:
|
||||
return '', 404
|
||||
|
||||
user_id = request.form['user_id']
|
||||
institution_id = request.form['institution_id']
|
||||
role = request.form['role']
|
||||
if role not in ['moderator', 'user']:
|
||||
return '', 404
|
||||
|
||||
# TODO: remove this restriction
|
||||
if len(portal.base.get_user_institutions(user_id)) > 0:
|
||||
return 'Uporabnik je že dodeljen instituciji. Dodeljevanje večim institucijam '\
|
||||
'zaenkrat ni implementirano.'
|
||||
|
||||
portal.base.add_user_to_institution(user_id, institution_id, role)
|
||||
return 'Uporabnik je bil dodeljen instituciji.'
|
||||
|
||||
@app.route('/<corpus_name>/delinstitution', methods=['POST'])
|
||||
@login_required
|
||||
def del_institution(corpus_name):
|
||||
# TODO: check if valid corpus_name
|
||||
# TODO: check if user is admin
|
||||
# TODO: delete cascade - institution, user_institution_mapping, corpus_access, institution_contract
|
||||
return '', 404
|
||||
|
||||
|
||||
@app.route('/<corpus_name>/upload', methods=['POST'])
|
||||
def handle_upload(corpus_name):
|
||||
if corpus_name not in ENABLED_CORPUSES:
|
||||
return 404
|
||||
return '', 404
|
||||
|
||||
if corpus_name == 'solar':
|
||||
if not current_user.is_authenticated:
|
||||
return 404
|
||||
return '', 404
|
||||
if not portal.base.has_user_corpus_access(current_user.id, corpus_name):
|
||||
return 404
|
||||
return '', 404
|
||||
return upload_handler_solar.handle_upload(request, current_user.get_id())
|
||||
elif corpus_name == 'predavanja':
|
||||
return upload_handler_predavanja.handle_upload(request)
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
"""Changed user<->institution<->corpus relational mapping.
|
||||
|
||||
Revision ID: 5ba116fc7f06
|
||||
Revises: 7d6db184b8fc
|
||||
Create Date: 2021-06-07 13:02:42.900168
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5ba116fc7f06'
|
||||
down_revision = '7d6db184b8fc'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('institution_contract',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('institution', sa.Integer(), nullable=False),
|
||||
sa.Column('corpus', sa.String(), nullable=False),
|
||||
sa.Column('timestamp', sa.DateTime(), nullable=False),
|
||||
sa.Column('file_contract', sa.String(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['institution'], ['institution.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_table('user_institution_mapping',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('user', sa.Integer(), nullable=False),
|
||||
sa.Column('institution', sa.Integer(), nullable=False),
|
||||
sa.Column('role', sa.String(), nullable=False),
|
||||
sa.ForeignKeyConstraint(['institution'], ['institution.id'], ),
|
||||
sa.ForeignKeyConstraint(['user'], ['registered_user.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.add_column('corpus_access', sa.Column('institution', sa.Integer(), nullable=False))
|
||||
op.drop_constraint('user_id_fkey', 'corpus_access', type_='foreignkey')
|
||||
op.create_foreign_key(None, 'corpus_access', 'institution', ['institution'], ['id'])
|
||||
op.drop_column('corpus_access', 'user_id')
|
||||
op.create_unique_constraint(None, 'institution', ['name'])
|
||||
op.drop_column('institution', 'file_contract')
|
||||
op.create_unique_constraint(None, 'registered_user', ['email'])
|
||||
op.drop_constraint('registered_user_institution_fkey', 'registered_user', type_='foreignkey')
|
||||
op.drop_column('registered_user', 'institution_moderator')
|
||||
op.drop_column('registered_user', 'institution')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('registered_user', sa.Column('institution', sa.INTEGER(), autoincrement=False, nullable=True))
|
||||
op.add_column('registered_user', sa.Column('institution_moderator', sa.BOOLEAN(), autoincrement=False, nullable=True))
|
||||
op.create_foreign_key('registered_user_institution_fkey', 'registered_user', 'institution', ['institution'], ['id'])
|
||||
op.drop_constraint(None, 'registered_user', type_='unique')
|
||||
op.add_column('institution', sa.Column('file_contract', sa.TEXT(), autoincrement=False, nullable=True))
|
||||
op.drop_constraint(None, 'institution', type_='unique')
|
||||
op.add_column('corpus_access', sa.Column('user_id', sa.INTEGER(), autoincrement=False, nullable=False))
|
||||
op.drop_constraint(None, 'corpus_access', type_='foreignkey')
|
||||
op.create_foreign_key('user_id_fkey', 'corpus_access', 'registered_user', ['user_id'], ['id'])
|
||||
op.drop_column('corpus_access', 'institution')
|
||||
op.drop_table('user_institution_mapping')
|
||||
op.drop_table('institution_contract')
|
||||
# ### end Alembic commands ###
|
|
@ -3,6 +3,7 @@ import time
|
|||
import ssl
|
||||
import traceback
|
||||
import re
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
|
@ -19,7 +20,9 @@ from email.mime.application import MIMEApplication
|
|||
import pdfkit
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from . model import db, UploadRegular, UploadSolar, RegisteredUser, CorpusAccess, Institution
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
from . model import db, UploadRegular, UploadSolar, RegisteredUser, UserInstitutionMapping, Institution, InstitutionContract, CorpusAccess
|
||||
|
||||
|
||||
#REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$')
|
||||
|
@ -131,7 +134,7 @@ class UploadHandler:
|
|||
return res
|
||||
|
||||
@staticmethod
|
||||
def store_model(self, model_obj):
|
||||
def store_model(model_obj):
|
||||
try:
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
|
@ -234,20 +237,100 @@ class UploadHandler:
|
|||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def get_user_institution(user_id):
|
||||
match = db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).one()
|
||||
return match.institution
|
||||
|
||||
def get_user_institutions(user_id):
|
||||
return UserInstitutionMapping.query.filter_by(user=user_id).all()
|
||||
|
||||
|
||||
def has_user_corpus_access(user_id, corpus_name):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
# TODO: check if user even is active?
|
||||
|
||||
# Admins always have access to everything.
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
return CorpusAccess.query.filter_by(user_id=user.id, corpus=corpus_name).first() is not None
|
||||
|
||||
# Check if user belongs to an institution, that has access to this corpus.
|
||||
institutions = get_user_institutions(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
|
||||
return has_access
|
||||
|
||||
|
||||
def is_admin(user_id):
|
||||
user = RegisteredUser.query.filter_by(id=user_id).first()
|
||||
if user.role == 'admin':
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_user_obj(user_id):
|
||||
return RegisteredUser.query.filter_by(id=user_id).first()
|
||||
|
||||
|
||||
def get_institution_obj(institution_id):
|
||||
return Institution.query.filter_by(id=institution_id).first()
|
||||
|
||||
|
||||
def register_new_user(name, email, password, active=True, admin=False):
|
||||
model_obj = RegisteredUser(
|
||||
name=name,
|
||||
email=email,
|
||||
role='admin' if admin else 'user',
|
||||
pass_hash=generate_password_hash(password),
|
||||
active=active,
|
||||
registered=datetime.now()
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_institution(name, region):
|
||||
model_obj = Institution(
|
||||
name=name,
|
||||
region=region
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def grant_institution_corpus_access(institution_id, corpus_name):
|
||||
model_obj = CorpusAccess(
|
||||
institution=institution_id,
|
||||
corpus=corpus_name
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def add_user_to_institution(user_id, institution_id, role):
|
||||
model_obj = UserInstitutionMapping(
|
||||
user=user_id,
|
||||
institution=institution_id,
|
||||
role=role
|
||||
)
|
||||
db.session.add(model_obj)
|
||||
db.session.commit()
|
||||
return model_obj.id
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
return RegisteredUser.query.filter_by(active=True).all()
|
||||
|
||||
|
||||
def is_institution_moderator(user_id, institution_id):
|
||||
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).first()
|
||||
if not user_inst_mapping:
|
||||
return False
|
||||
if user_inst_mapping.role != 'moderator':
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
|
@ -83,31 +83,45 @@ class ContractsSolar(db.Model):
|
|||
contract_type = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class CorpusAccess(db.Model):
|
||||
__tablename__ = 'corpus_access'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_id = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
|
||||
corpus = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class RegisteredUser(UserMixin, db.Model):
|
||||
__tablename__ = 'registered_user'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String, nullable=False)
|
||||
email = db.Column(db.String, nullable=False)
|
||||
email = db.Column(db.String, nullable=False, unique=True)
|
||||
role = db.Column(db.String, nullable=False)
|
||||
pass_hash = db.Column(db.String, nullable=False)
|
||||
active = db.Column(db.Boolean, nullable=True)
|
||||
last_login = db.Column(db.DateTime, nullable=True)
|
||||
registered = db.Column(db.DateTime, nullable=True)
|
||||
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=True)
|
||||
institution_moderator = db.Column(db.Boolean, default=False)
|
||||
|
||||
|
||||
class UserInstitutionMapping(db.Model):
|
||||
__tablename__ = 'user_institution_mapping'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
|
||||
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False)
|
||||
role =db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class Institution(db.Model):
|
||||
__tablename__ = 'institution'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String, nullable=False)
|
||||
name = db.Column(db.String, nullable=False, unique=True)
|
||||
region = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class CorpusAccess(db.Model):
|
||||
__tablename__ = 'corpus_access'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False)
|
||||
corpus = db.Column(db.String, nullable=False)
|
||||
|
||||
|
||||
class InstitutionContract(db.Model):
|
||||
__tablename__ = 'institution_contract'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False)
|
||||
corpus = db.Column(db.String, nullable=False)
|
||||
timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
file_contract = db.Column(db.String, nullable=True)
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ import re
|
|||
import traceback
|
||||
import hashlib
|
||||
from datetime import datetime
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy import desc, exists
|
||||
|
||||
from portal.base import UploadHandler
|
||||
from portal.model import db, UploadSolar, ContractsSolar, RegisteredUser, Institution
|
||||
from portal.base import UploadHandler, get_user_institutions, has_user_corpus_access
|
||||
from portal.model import db, UploadSolar, ContractsSolar, RegisteredUser, Institution, InstitutionContract, UserInstitutionMapping, CorpusAccess
|
||||
|
||||
|
||||
VALID_PROGRAMS = {'OS', 'SSG', 'MGP', 'ZG', 'NPI', 'SPI', 'SSI', 'PTI'}
|
||||
|
@ -27,7 +27,8 @@ class UploadHandlerSolar(UploadHandler):
|
|||
sorted_f_hashes = list(file_hashes.values())
|
||||
sorted_f_hashes.sort()
|
||||
|
||||
institution_id = UploadHandler.get_user_institution(user_id)
|
||||
# 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
|
||||
|
||||
model_obj = UploadSolar(
|
||||
upload_user = user_id,
|
||||
|
@ -44,7 +45,7 @@ class UploadHandlerSolar(UploadHandler):
|
|||
grammar_corrections=form_data['jezikovni-popravki'],
|
||||
upload_file_hashes=sorted_f_hashes
|
||||
)
|
||||
self.store_model(model_obj)
|
||||
UploadHandler.store_model(model_obj)
|
||||
|
||||
def handle_upload(self, request, user_id):
|
||||
err = self.check_upload_request(request)
|
||||
|
@ -73,9 +74,9 @@ class UploadHandlerSolar(UploadHandler):
|
|||
return 'Uspešno ste oddali datotek(e). Št. datotek: {}'.format(len(request.files))
|
||||
|
||||
def handle_contract_upload(self, request, user_id):
|
||||
contracts_type = request.form['tip-pogodbe']
|
||||
contract_type = request.form['tip-pogodbe']
|
||||
|
||||
if contracts_type not in ['sola', 'ucenci-starsi']:
|
||||
if contract_type not in ['sola', 'ucenci-starsi']:
|
||||
return 'Neveljaven tip pogodbe.'
|
||||
|
||||
f_obj = None
|
||||
|
@ -92,34 +93,45 @@ class UploadHandlerSolar(UploadHandler):
|
|||
return 'Niste naložili nobene datoteke.'
|
||||
|
||||
base = self.get_uploads_subdir('contracts')
|
||||
f_hash = hashlib.md5(f_obj.read())
|
||||
f_hash = hashlib.md5(f_obj.read()).hexdigest()
|
||||
f_obj.seek(0, 0)
|
||||
|
||||
# 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()
|
||||
|
||||
path = sub_dir / f_hash[2:] + '.pdf'
|
||||
path = sub_dir / (f_hash[2:] + '.pdf')
|
||||
f_obj.save(path)
|
||||
|
||||
timestamp = datetime.now()
|
||||
user_obj = RegisteredUser.query.filter_by(id=user_id).one()
|
||||
institution_id = user_obj.institution
|
||||
is_institution_moderator = user_obj.institution_moderator
|
||||
|
||||
if institution_id is None:
|
||||
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_moderator = True if user_institution_mapping.role == 'moderator' else False
|
||||
|
||||
if contracts_type == 'sola':
|
||||
if contract_type == 'sola':
|
||||
if not is_institution_moderator:
|
||||
return 'Vaš uporabnik nima pravic za nalaganje pogodbe s šolo.'
|
||||
Institution.update().values(file_contract=f_hash).where(id=institution_id)
|
||||
|
||||
# TODO: insert institution contract
|
||||
model_obj = InstitutionContract(
|
||||
institution=institution_id,
|
||||
corpus='solar',
|
||||
timestamp=timestamp,
|
||||
file_contract=f_hash
|
||||
)
|
||||
self.store_model(model_obj)
|
||||
else:
|
||||
model_obj = ContractsSolar(
|
||||
institution=institution_id,
|
||||
upload_user=user_id,
|
||||
timestamp=timestamp,
|
||||
file_contract=f_hash,
|
||||
contracts_type=contracts_type,
|
||||
contract_type=contract_type,
|
||||
)
|
||||
|
||||
self.store_model(model_obj)
|
||||
return 'Nalaganje pogodbe je bilo uspešno.'
|
||||
|
||||
|
@ -154,12 +166,30 @@ 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_all_institutions():
|
||||
# TODO: do filtering purely within an SQL query
|
||||
res = []
|
||||
for institution in Institution.query.all():
|
||||
row = CorpusAccess.query.filter_by(institution=institution.id, corpus='solar').first()
|
||||
if row:
|
||||
res.append(institution)
|
||||
return res
|
||||
|
||||
|
||||
def get_institution_student_contracts(institution_id):
|
||||
return ContractsSolar.query.filter_by(id=institution_id, contract_type='ucenci-starsi').all()
|
||||
return ContractsSolar.query.filter_by(institution=institution_id, contract_type='ucenci-starsi').all()
|
||||
|
||||
|
||||
def get_institution_contract(institution_id):
|
||||
return ContractsSolar.query.filter_by(id=institution_id, contract_type='sola').first()
|
||||
return InstitutionContract.query.filter_by(institution=institution_id, corpus='solar').order_by(desc(InstitutionContract.timestamp)).first()
|
||||
|
||||
|
||||
def get_all_active_users():
|
||||
# TODO: do filtering purely within an SQL query
|
||||
res = []
|
||||
active_users = RegisteredUser.query.filter_by(active=True).all()
|
||||
for user in active_users:
|
||||
if has_user_corpus_access(user.id, 'solar'):
|
||||
res.append(user)
|
||||
return res
|
||||
|
||||
|
|
|
@ -84,6 +84,20 @@ html {
|
|||
color: #006cb7;
|
||||
}
|
||||
|
||||
.section-desc {
|
||||
font-family: Roboto;
|
||||
font-style: bold;
|
||||
font-weight: 320;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
margin-block-start: 0.4em;
|
||||
border-bottom: 3px solid #006cb7;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
color: #006cb7;
|
||||
}
|
||||
|
||||
label {
|
||||
font-family: Roboto;
|
||||
font-style: normal;
|
||||
|
|
110
templates/solar-admin.html
Normal file
110
templates/solar-admin.html
Normal file
|
@ -0,0 +1,110 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Admin panel - Šolar</title>
|
||||
<style>
|
||||
.tableFixHead {
|
||||
overflow-y: auto;
|
||||
height: 106px;
|
||||
}
|
||||
.tableFixHead thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 8px 16px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
th {
|
||||
background: #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Uporabniki</h2>
|
||||
<h3>Dodaj uporabnika</h3>
|
||||
<form action="/solar/adduser" method="post">
|
||||
<label for="name">Ime in priimek:</label><br>
|
||||
<input type="text" id="name" name="name"><br>
|
||||
<label for="email">Email:</label><br>
|
||||
<input type="text" id="email" name="email"><br>
|
||||
<label for="password">Geslo:</label><br>
|
||||
<input type="text" id="password" name="password"><br>
|
||||
<input type="submit" value="Dodaj">
|
||||
</form>
|
||||
<!--<h3>Odstrani uporabnika</h3>
|
||||
<form action="/solar/deluser" method="post">
|
||||
<label for="user_id">ID uporabnika:</label><br>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<input type="submit" value="Odstrani">
|
||||
</form>-->
|
||||
<h3>Seznam vseh aktivnih uporabnikov</h3>
|
||||
<div class="tableFixHead">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Ime in priimek</th>
|
||||
<th>Email</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in users %}
|
||||
<tr>
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.email}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
<h3>Dodeli uporabnika instituciji</h3>
|
||||
<form action="/solar/addusertoinstitution" method="post">
|
||||
<label for="user_id">ID uporabnika:</label>
|
||||
<input type="text" id="user_id" name="user_id"><br>
|
||||
<label for="institution_id">ID institucije:</label>
|
||||
<input type="text" id="institution_id" name="institution_id"><br>
|
||||
<label for="role">Vloga v instituciji:</label>
|
||||
<select name="role" id="role">
|
||||
<option value="moderator">Moderator</option>
|
||||
<option value="user">Uporabnik</option>
|
||||
</select>
|
||||
<input type="submit" value="Dodeli">
|
||||
</form>
|
||||
<div> </div>
|
||||
<h2>Institucije</h2>
|
||||
<h3>Dodaj institucijo</h3>
|
||||
<form action="/solar/addinstitution" method="post">
|
||||
<label for="name">Naziv:</label>
|
||||
<input type="text" id="name" name="name"><br>
|
||||
<label for="region">Regija:</label>
|
||||
<input type="text" id="region" name="region"><br>
|
||||
<input type="submit" value="Dodaj">
|
||||
</form>
|
||||
<h3>Seznam vseh instituticij</h3>
|
||||
<div class="tableFixHead">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Naziv</th>
|
||||
<th>Regija</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in institutions %}
|
||||
<tr>
|
||||
<td>{{item.id}}</td>
|
||||
<td>{{item.name}}</td>
|
||||
<td>{{item.region}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
13
templates/solar-institution-managment.html
Normal file
13
templates/solar-institution-managment.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Admin panel - Šolar</title>
|
||||
</head>
|
||||
<body>
|
||||
<h3>Uporabniki</h3>
|
||||
TODO: odobri registracije
|
||||
<form> </form>
|
||||
<div> </div>
|
||||
|
||||
</body>
|
|
@ -18,6 +18,7 @@
|
|||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<div class="bg"></div>
|
||||
<div id="main-window">
|
||||
<div id="rect1">
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<div class="bg"></div>
|
||||
<div id="main-window">
|
||||
<div id="rect1">
|
||||
|
@ -23,32 +24,48 @@
|
|||
<div id="conract-container" style="padding: 20px;">
|
||||
{% if contract_school %}
|
||||
<div class="contract-item" style="background-color: #ccffcc;">
|
||||
<div class="contract-item-icon">...</div>
|
||||
<div class="contract-item-title">Pogodba o prenosu lastništva</div>
|
||||
<div class="contract-item-icon"></div>
|
||||
<div class="contract-item-title">Pogodba s šolo</div>
|
||||
<div class="contract-item-date">DODANO {{contract_school.date}}</div>
|
||||
<a href="/solar/pogodbe/{{ contract_school.file_hash }}.pdf" class="contract-item-button">PRENESI</div>
|
||||
<a href="/solar/pogodbe/{{ contract_school.file_contract }}.pdf" class="contract-item-button">PRENESI</a>
|
||||
</div>
|
||||
</br>
|
||||
{% endif %}
|
||||
{% for item in contracts_students %}
|
||||
<div class="contract-item">
|
||||
<div class="contract-item-icon">...</div>
|
||||
<div class="contract-item-icon"></div>
|
||||
<div class="contract-item-title">Pogodba o prenosu lastništva</div>
|
||||
<div class="contract-item-date">DODANO {{item.date}}</div>
|
||||
<a href="/solar/pogodbe/{{ item.file_hash }}.pdf" class="contract-item-button">PRENESI</div>
|
||||
<a href="/solar/pogodbe/{{ item.file_contract }}.pdf" class="contract-item-button">PRENESI</a>
|
||||
</div>
|
||||
</br>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</br>
|
||||
</br>
|
||||
{% if show_upload_form %}
|
||||
<form action="/solar/pogodbe">
|
||||
<input type="radio" id="sola" name="tip-pogodbe" value="sola">
|
||||
<label for="sola">Pogodba s šolo</label><br>
|
||||
<input type="radio" id="ucenci-starsi" name="tip-pogodbe" value="ucenci-starsi">
|
||||
<label for="sola">Pogodba z učenci / starši</label><br>
|
||||
<input type="file" id="file-contract" name="filename">
|
||||
<input type="submit" text="Naloži pogodbo">
|
||||
<div style="padding: 20px; position: absolute; top: 500px; width: 89%;">
|
||||
<div class="section-desc">Oddaj pogodbo</div>
|
||||
<form action="/solar/pogodbe" method="post" enctype="multipart/form-data">
|
||||
{% if enable_upload_school_contract %}
|
||||
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center">
|
||||
<label style="width: 80%; text-align: right;" for="sola">Pogodba s šolo</label>
|
||||
<input style="width: 20%;" type="radio" id="sola" name="tip-pogodbe" value="sola">
|
||||
</div>
|
||||
{% else %}
|
||||
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center">
|
||||
<label style="width: 80%; text-align: right;" for="sola">Pogodba s šolo</label>
|
||||
<input style="width: 20%;"type="radio" id="sola" name="tip-pogodbe" value="sola" disabled>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center">
|
||||
<label style="width: 80%; text-align: right;" for="ucenci-starsi">Pogodba z učenci / starši</label>
|
||||
<input style="width: 20%;" type="radio" id="ucenci-starsi" name="tip-pogodbe" value="ucenci-starsi" checked>
|
||||
</div>
|
||||
<input style="font-size: 10px;" type="file" id="file-contract" name="filename">
|
||||
<button style="float: right;" type="submit">Oddaj pogodbo</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div id="rect2" class="mock-side">
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<link rel="stylesheet" href="/static/style.css" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<a href="/solar/logout">Odjavi se</a>
|
||||
<div class="bg"></div>
|
||||
<div id="main-window">
|
||||
<div id="rect1">
|
||||
|
|
Loading…
Reference in New Issue
Block a user