solar update

This commit is contained in:
msinkec 2021-06-08 08:00:18 +02:00
parent 31ce97cb44
commit 6697c60c7f
11 changed files with 554 additions and 84 deletions

169
app.py
View File

@ -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)

View File

@ -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 ###

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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
View 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>

View 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>

View File

@ -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">

View File

@ -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">

View File

@ -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">