Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
protokollsystem
proto3
Commits
1e997477
Commit
1e997477
authored
Feb 23, 2017
by
Robin Sonnabend
Browse files
configurable fonts and document upload
parent
d558fb90
Changes
11
Hide whitespace changes
Inline
Side-by-side
config.py.example
View file @
1e997477
SQLALCHEMY_DATABASE_URI = "postgresql://proto3:@/proto3"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SECRET_KEY = "abc"
DEBUG = False
MAIL_ACTIVE = True
MAIL_FROM = "protokolle@example.com"
MAIL_HOST = "mail.example.com:465"
MAIL_USER = "user"
MAIL_PASSWORD = "password"
MAIL_PREFIX = "protokolle"
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_TASK_SERIALIZER = "pickle"
CELERY_ACCEPT_CONTENT = ["pickle"]
URL_ROOT = "protokolle.example.com"
URL_PROTO = "https"
URL_PATH = "/"
URL_PARAMS = ""
ERROR_CONTEXT_LINES = 3
# choose something nice from fc-list
FONTS = {
"main": {
"regular": "Nimbus Sans",
"bold": "NimbusSans",
"italic": "NimbusSans",
"bolditalic": "NimbusSans"
},
"roman": {
"regular": "Nimbus Roman",
"bold": "Nimbus Roman",
"italic": "Nimbus Roman",
"bolditalic": "Nimbus Roman"
},
"sans": {
"regular": "Nimbus Sans",
"bold": "NimbusSans",
"italic": "NimbusSans",
"bolditalic": "NimbusSans"
},
"mono": {
"regular": "Nimbus Mono PS",
"bold": "Nimbus Mono PS",
"italic": "Nimbus Mono PS",
"bolditalic": "Nimbus Mono PS"
}
}
migrations/versions/b114754024fb_.py
0 → 100644
View file @
1e997477
"""empty message
Revision ID: b114754024fb
Revises: 97cf1913e60d
Create Date: 2017-02-23 20:33:56.446729
"""
from
alembic
import
op
import
sqlalchemy
as
sa
# revision identifiers, used by Alembic.
revision
=
'b114754024fb'
down_revision
=
'97cf1913e60d'
branch_labels
=
None
depends_on
=
None
def
upgrade
():
# ### commands auto generated by Alembic - please adjust! ###
op
.
add_column
(
'documents'
,
sa
.
Column
(
'is_private'
,
sa
.
Boolean
(),
nullable
=
True
))
# ### end Alembic commands ###
def
downgrade
():
# ### commands auto generated by Alembic - please adjust! ###
op
.
drop_column
(
'documents'
,
'is_private'
)
# ### end Alembic commands ###
models/database.py
View file @
1e997477
...
...
@@ -6,9 +6,10 @@ import math
from
shared
import
db
from
utils
import
random_string
,
url_manager
#from models.tables import TexResponsiblesTable, TexSupportersTable
import
os
from
sqlalchemy.orm
import
relationship
,
backref
from
sqlalchemy
import
event
from
sqlalchemy.orm
import
relationship
,
backref
,
sessionmaker
import
config
...
...
@@ -171,16 +172,28 @@ class Document(db.Model):
name
=
db
.
Column
(
db
.
String
)
filename
=
db
.
Column
(
db
.
String
,
unique
=
True
)
is_compiled
=
db
.
Column
(
db
.
Boolean
)
is_private
=
db
.
Column
(
db
.
Boolean
)
def
__init__
(
self
,
protocol_id
,
name
,
filename
,
is_compiled
):
def
__init__
(
self
,
protocol_id
,
name
,
filename
,
is_compiled
,
is_private
):
self
.
protocol_id
=
protocol_id
self
.
name
=
name
self
.
filename
=
filename
self
.
is_compiled
=
is_compiled
self
.
is_private
=
is_private
def
__repr__
(
self
):
return
"<Document(id={}, protocol_id={}, name={}, filename={}, is_compiled={})>"
.
format
(
self
.
id
,
self
.
protocol_id
,
self
.
name
,
self
.
filename
,
self
.
is_compiled
)
return
"<Document(id={}, protocol_id={}, name={}, filename={}, is_compiled={}, is_private={})>"
.
format
(
self
.
id
,
self
.
protocol_id
,
self
.
name
,
self
.
filename
,
self
.
is_compiled
,
self
.
is_private
)
def
get_filename
(
self
):
return
os
.
path
.
join
(
config
.
DOCUMENTS_PATH
,
self
.
filename
)
@
event
.
listens_for
(
Document
,
"before_delete"
)
def
on_delete
(
mapper
,
connection
,
document
):
if
document
.
filename
is
not
None
:
document_path
=
os
.
path
.
join
(
config
.
DOCUMENTS_PATH
,
document
.
filename
)
if
os
.
path
.
isfile
(
document_path
):
os
.
remove
(
document_path
)
class
Todo
(
db
.
Model
):
__tablename__
=
"todos"
...
...
server.py
View file @
1e997477
...
...
@@ -3,6 +3,7 @@ import locale
locale
.
setlocale
(
locale
.
LC_TIME
,
"de_DE.utf8"
)
from
flask
import
Flask
,
g
,
current_app
,
request
,
session
,
flash
,
redirect
,
url_for
,
abort
,
render_template
,
Response
,
send_file
from
werkzeug.utils
import
secure_filename
from
flask_script
import
Manager
,
prompt
from
flask_migrate
import
Migrate
,
MigrateCommand
#from flask_socketio import SocketIO
...
...
@@ -10,13 +11,14 @@ from celery import Celery
from
functools
import
wraps
import
requests
from
io
import
StringIO
,
BytesIO
import
os
import
config
from
shared
import
db
,
date_filter
,
datetime_filter
,
date_filter_long
,
time_filter
,
ldap_manager
,
security_manager
from
utils
import
is_past
,
mail_manager
,
url_manager
from
models.database
import
ProtocolType
,
Protocol
,
DefaultTOP
,
TOP
,
Document
,
Todo
,
Decision
,
MeetingReminder
,
Error
from
views.forms
import
LoginForm
,
ProtocolTypeForm
,
DefaultTopForm
,
MeetingReminderForm
,
NewProtocolForm
from
views.tables
import
ProtocolsTable
,
ProtocolTypesTable
,
ProtocolTypeTable
,
DefaultTOPsTable
,
MeetingRemindersTable
,
ErrorsTable
,
TodosTable
from
views.forms
import
LoginForm
,
ProtocolTypeForm
,
DefaultTopForm
,
MeetingReminderForm
,
NewProtocolForm
,
DocumentUploadForm
from
views.tables
import
ProtocolsTable
,
ProtocolTypesTable
,
ProtocolTypeTable
,
DefaultTOPsTable
,
MeetingRemindersTable
,
ErrorsTable
,
TodosTable
,
DocumentsTable
app
=
Flask
(
__name__
)
app
.
config
.
from_object
(
config
)
...
...
@@ -335,7 +337,9 @@ def show_protocol(protocol_id):
flash
(
"Invalides Protokoll."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
errors_table
=
ErrorsTable
(
protocol
.
errors
)
return
render_template
(
"protocol-show.html"
,
protocol
=
protocol
,
errors_table
=
errors_table
)
documents_table
=
DocumentsTable
(
protocol
.
documents
)
document_upload_form
=
DocumentUploadForm
()
return
render_template
(
"protocol-show.html"
,
protocol
=
protocol
,
errors_table
=
errors_table
,
documents_table
=
documents_table
,
document_upload_form
=
document_upload_form
)
@
app
.
route
(
"/protocol/etherpull/<int:protocol_id>"
)
def
etherpull_protocol
(
protocol_id
):
...
...
@@ -345,10 +349,7 @@ def etherpull_protocol(protocol_id):
flash
(
"Invalides Protokoll oder keine Berechtigung."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
source_req
=
requests
.
get
(
protocol
.
get_etherpad_source_link
())
#source = source_req.content.decode("utf-8")
source
=
source_req
.
text
print
(
source
.
split
(
"
\r
"
))
#print(source.split("\n"))
protocol
.
source
=
source
db
.
session
.
commit
()
tasks
.
parse_protocol
(
protocol
)
...
...
@@ -386,6 +387,49 @@ def list_todos():
todos_table
=
TodosTable
(
todos
)
return
render_template
(
"todos-list.html"
,
todos
=
todos
,
todos_table
=
todos_table
)
@
app
.
route
(
"/document/download/<int:document_id>"
)
def
download_document
(
document_id
):
user
=
current_user
()
document
=
Document
.
query
.
filter_by
(
id
=
document_id
).
first
()
if
document
is
None
:
flash
(
"Invalides Dokument."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
if
((
document
.
is_private
and
not
document
.
protocol
.
protocoltype
.
has_private_view_right
(
user
))
or
(
not
document
.
is_private
and
not
document
.
protocol
.
protocoltype
.
has_public_view_right
(
user
))):
flash
(
"Keine Berechtigung."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
with
open
(
document
.
get_filename
(),
"rb"
)
as
file
:
file_like
=
BytesIO
(
file
.
read
())
return
send_file
(
file_like
,
cache_timeout
=
1
,
as_attachment
=
True
,
attachment_filename
=
document
.
name
)
@
app
.
route
(
"/document/upload/<int:protocol_id>"
,
methods
=
[
"POST"
])
@
login_required
def
upload_document
(
protocol_id
):
user
=
current_user
()
protocol
=
Protocol
.
query
.
filter_by
(
id
=
protocol_id
).
first
()
if
protocol
is
None
or
not
protocol
.
protocoltype
.
has_modify_right
(
user
):
flash
(
"Insufficient permissions."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
form
=
DocumentUploadForm
()
print
(
form
,
form
.
document
.
data
,
form
.
private
.
data
)
print
(
request
.
files
)
if
form
.
document
.
data
is
None
:
flash
(
"No file has been selected."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
file
=
form
.
document
.
data
if
file
.
filename
==
""
:
flash
(
"No file has been selected."
,
"alert-error"
)
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"index"
))
if
file
:
filename
=
secure_filename
(
file
.
filename
)
internal_filename
=
"{}-{}"
.
format
(
protocol
.
id
,
filename
)
file
.
save
(
os
.
path
.
join
(
config
.
DOCUMENTS_PATH
,
internal_filename
))
document
=
Document
(
protocol
.
id
,
filename
,
internal_filename
,
False
,
form
.
private
.
data
)
db
.
session
.
add
(
document
)
db
.
session
.
commit
()
return
redirect
(
request
.
args
.
get
(
"next"
)
or
url_for
(
"show_protocol"
,
protocol_id
=
protocol
.
id
))
@
app
.
route
(
"/login"
,
methods
=
[
"GET"
,
"POST"
])
...
...
shared.py
View file @
1e997477
...
...
@@ -69,6 +69,8 @@ def datetime_filter(date):
return
date
.
strftime
(
"%d. %B %Y, %H:%M"
)
def
date_filter_long
(
date
):
return
date
.
strftime
(
"%A, %d.%m.%Y, Kalenderwoche %W"
)
def
date_filter_short
(
date
):
return
date
.
strftime
(
"%d.%m.%Y"
)
def
time_filter
(
time
):
return
time
.
strftime
(
"%H:%m"
)
...
...
tasks.py
View file @
1e997477
...
...
@@ -7,7 +7,7 @@ import tempfile
from
models.database
import
Document
,
Protocol
,
Error
,
Todo
,
Decision
,
TOP
,
DefaultTOP
from
server
import
celery
,
app
from
shared
import
db
,
escape_tex
,
unhyphen
,
date_filter
,
datetime_filter
,
date_filter_long
,
time_filter
,
class_filter
from
shared
import
db
,
escape_tex
,
unhyphen
,
date_filter
,
datetime_filter
,
date_filter_long
,
date_filter_short
,
time_filter
,
class_filter
from
utils
import
mail_manager
,
url_manager
,
encode_kwargs
,
decode_kwargs
from
parser
import
parse
,
ParserException
,
Element
,
Content
,
Text
,
Tag
,
Remark
,
Fork
...
...
@@ -27,6 +27,7 @@ texenv.lstrip_blocks = True
texenv
.
filters
[
"url_complete"
]
=
url_manager
.
complete
texenv
.
filters
[
"datify"
]
=
date_filter
texenv
.
filters
[
"datify_long"
]
=
date_filter_long
texenv
.
filters
[
"datify_short"
]
=
date_filter_short
texenv
.
filters
[
"datetimify"
]
=
datetime_filter
texenv
.
filters
[
"timify"
]
=
time_filter
texenv
.
filters
[
"class"
]
=
class_filter
...
...
@@ -182,7 +183,9 @@ def compile_async(content, protocol_id):
log_filename
=
"protocol.log"
with
open
(
os
.
path
.
join
(
compile_dir
,
protocol_source_filename
),
"w"
)
as
source_file
:
source_file
.
write
(
content
)
shutil
.
copy
(
"static/tex/protokoll2.cls"
,
compile_dir
)
protocol2_class_source
=
texenv
.
get_template
(
"protokoll2.cls"
).
render
(
fonts
=
config
.
FONTS
)
with
open
(
os
.
path
.
join
(
compile_dir
,
"protokoll2.cls"
),
"w"
)
as
protocol2_class_file
:
protocol2_class_file
.
write
(
protocol2_class_source
)
os
.
chdir
(
compile_dir
)
command
=
[
"/usr/bin/xelatex"
,
...
...
@@ -196,12 +199,12 @@ def compile_async(content, protocol_id):
for
old_document
in
[
document
for
document
in
protocol
.
documents
if
document
.
is_compiled
]:
protocol
.
documents
.
remove
(
old_document
)
db
.
session
.
commit
()
document
=
Document
(
protocol
.
id
,
name
=
"protokoll_{}_{}.pdf"
.
format
(
protocol
.
protocoltype
.
short_name
,
date_filter
(
protocol
.
date
)),
filename
=
""
,
is_compiled
=
True
)
document
=
Document
(
protocol
.
id
,
name
=
"protokoll_{}_{}.pdf"
.
format
(
protocol
.
protocoltype
.
short_name
,
date_filter
_short
(
protocol
.
date
)),
filename
=
""
,
is_compiled
=
True
,
is_private
=
False
)
db
.
session
.
add
(
document
)
db
.
session
.
commit
()
target_filename
=
"compiled-{}.pdf"
.
format
(
document
.
id
)
document
.
filename
=
target_filename
shutil
.
copy
(
os
.
path
.
join
(
compile_dir
,
protocol_target_filename
),
os
.
path
.
join
(
"documents"
,
target_filename
))
shutil
.
copy
(
os
.
path
.
join
(
compile_dir
,
protocol_target_filename
),
os
.
path
.
join
(
config
.
DOCUMENTS_PATH
,
target_filename
))
db
.
session
.
commit
()
shutil
.
copy
(
os
.
path
.
join
(
compile_dir
,
log_filename
),
"/tmp"
)
except
subprocess
.
SubprocessError
:
...
...
templates/macros.html
View file @
1e997477
...
...
@@ -87,9 +87,9 @@ to not render a label for the CRSFTokenField -->
action_text - text of submit button
class_ - sets a class for form
#}
{% macro render_form(form, action_url='', action_text='Submit', class_='', btn_class='btn btn-default') -%}
{% macro render_form(form, action_url='', action_text='Submit', class_='', btn_class='btn btn-default'
, enctype=None
) -%}
<form
method=
"POST"
action=
"{{ action_url }}"
role=
"form"
class=
"{{ class_ }}"
>
<form
method=
"POST"
action=
"{{ action_url }}"
role=
"form"
class=
"{{ class_ }}"
{%
if
enctype
is
not
none
%}
enctype=
"{{enctype}}"
{%
endif
%}
>
{{ form.hidden_tag() if form.hidden_tag }}
{% if caller %}
{{ caller() }}
...
...
templates/protocol-show.html
View file @
1e997477
{% extends "layout.html" %}
{% from "macros.html" import render_table %}
{% from "macros.html" import render_table
, render_form
%}
{% block title %}Protokoll{% endblock %}
{% block content %}
...
...
@@ -71,8 +71,10 @@
{{render_table(errors_table)}}
{% endif %}
{% if protocol.documents|length > 0 %}
<h3>
Anhang
</h3>
{# TODO: render documents table here #}
{{render_table(documents_table)}}
{% endif %}
{% if protocol.is_done() %}
{{render_form(document_upload_form, action_url=url_for("upload_document", protocol_id=protocol.id, next=url_for("show_protocol", protocol_id=protocol.id)), action_text="Hochladen", enctype="multipart/form-data")}}
{% endif %}
</div>
</div>
...
...
static/tex
/protokoll2.cls
→
templates
/protokoll2.cls
View file @
1e997477
...
...
@@ -14,37 +14,41 @@
\LoadClass
[a4paper]
{
article
}
% so ein paar sachen die wir eh immer brauchen
%\RequirePackage[T1]{fontenc}
%\RequirePackage{ngerman}
\RequirePackage
[ngerman]
{
babel
}
%\RequirePackage[utf8]{inputenc}
\RequirePackage
[vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm]
{
geometry
}
%\RequirePackage{lineno}
\RequirePackage
{
longtable
}
\RequirePackage
{
framed
}
\RequirePackage
{
eurosym
}
\RequirePackage
[babel]
{
csquotes
}
%\RequirePackage{fontspec}
\RequirePackage
{
polyglossia
}
\setmainlanguage
[babelshorthands=true]
{
german
}
\RequirePackage
{
fontspec
}
%\setromanfont[Scale=0.925]{DejaVu Serif}
%\setsansfont[Scale=0.925]{DejaVu Sans}
%\setmonofont[Scale=0.925]{DejaVu Sans Mono}
%\setmainfont[Scale=0.925]{DejaVu Sans}
\setromanfont
{
Nimbus Roman
}
\setsansfont
{
Nimbus Sans
}
\setmonofont
{
Nimbus Mono PS
}
\setromanfont
[
BoldFont=
{
\VAR
{
fonts.roman.bold
}}
,
ItalicFont=
{
\VAR
{
fonts.roman.italic
}}
,
BoldItalicFont=
{
\VAR
{
fonts.roman.bolditalic
}}
]
{
\VAR
{
fonts.roman.regular
}}
\setsansfont
[
BoldFont=
{
\VAR
{
fonts.sans.bold
}}
,
ItalicFont=
{
\VAR
{
fonts.sans.italic
}}
,
BoldItalicFont=
{
\VAR
{
fonts.sans.bolditalic
}}
]
{
\VAR
{
fonts.sans.regular
}}
\setmonofont
[
BoldFont=
{
\VAR
{
fonts.mono.bold
}}
,
ItalicFont=
{
\VAR
{
fonts.mono.italic
}}
,
BoldItalicFont=
{
\VAR
{
fonts.mono.bolditalic
}}
]
{
\VAR
{
fonts.mono.regular
}}
\setmainfont
[
BoldFont=
{
NimbusSans
}
,
ItalicFont=
{
NimbusSans
}
,
BoldItalicFont=
{
NimbusSans
}
]
{
Nimbus Sans
}
% TODO: make configurable
BoldFont=
{
\VAR
{
fonts.main.bold
}
}
,
ItalicFont=
{
\VAR
{
fonts.main.italic
}
}
,
BoldItalicFont=
{
\VAR
{
fonts.main.bolditalic
}
}
]
{
\VAR
{
fonts.main.regular
}}
% nicht einr
�
cken und benutzerinnendefinierte kopfzeile
% nicht einr
ü
cken und benutzerinnendefinierte kopfzeile
\setlength
{
\parindent
}{
0cm
}
\setlength
{
\parskip
}{
1ex
}
\pagestyle
{
myheadings
}
...
...
views/forms.py
View file @
1e997477
from
flask_wtf
import
FlaskForm
from
wtforms
import
StringField
,
PasswordField
,
BooleanField
,
DateField
,
HiddenField
,
IntegerField
,
SelectField
from
wtforms
import
StringField
,
PasswordField
,
BooleanField
,
DateField
,
HiddenField
,
IntegerField
,
SelectField
,
FileField
from
wtforms.validators
import
InputRequired
class
LoginForm
(
FlaskForm
):
...
...
@@ -32,3 +32,7 @@ class NewProtocolForm(FlaskForm):
def
__init__
(
self
,
protocoltypes
,
**
kwargs
):
super
().
__init__
(
**
kwargs
)
self
.
protocoltype
.
choices
=
[(
protocoltype
.
id
,
protocoltype
.
short_name
)
for
protocoltype
in
protocoltypes
]
class
DocumentUploadForm
(
FlaskForm
):
document
=
FileField
(
"Datei"
,
validators
=
[
InputRequired
(
"Du musst eine Datei angeben."
)])
private
=
BooleanField
(
"Intern"
)
views/tables.py
View file @
1e997477
# coding: utf-8
from
flask
import
Markup
,
url_for
,
request
from
models.database
import
Protocol
,
ProtocolType
,
DefaultTOP
,
TOP
,
Todo
,
Decision
from
shared
import
date_filter
,
datetime_filter
from
shared
import
date_filter
,
datetime_filter
,
date_filter_short
class
Table
:
def
__init__
(
self
,
title
,
values
,
newlink
=
None
,
newtext
=
None
):
...
...
@@ -174,3 +174,17 @@ class TodosTable(Table):
todo
.
who
,
todo
.
description
]
class
DocumentsTable
(
Table
):
def
__init__
(
self
,
documents
):
super
().
__init__
(
"Anhang"
,
documents
)
def
headers
(
self
):
return
[
"ID"
,
"Name"
,
""
]
def
row
(
self
,
document
):
return
[
document
.
id
,
Table
.
link
(
url_for
(
"download_document"
,
document_id
=
document
.
id
),
document
.
name
),
""
]
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment