Skip to content
GitLab
About GitLab
GitLab: the DevOps platform
Explore GitLab
Install GitLab
How GitLab compares
Get started
GitLab docs
GitLab Learn
Pricing
Talk to an expert
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Projects
Groups
Snippets
Sign up now
Login
Sign in
Toggle navigation
Menu
Open sidebar
protokollsystem
proto3
Commits
b3f71d25
Commit
b3f71d25
authored
Feb 25, 2017
by
Robin Sonnabend
Browse files
Pushing to Wiki
parent
a84e567c
Changes
10
Hide whitespace changes
Inline
Side-by-side
config.py.example
View file @
b3f71d25
SQLALCHEMY_DATABASE_URI = "postgresql://proto3:@/proto3"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_DATABASE_URI = "postgresql://proto3:@/proto3"
# change
SQLALCHEMY_TRACK_MODIFICATIONS = False
# do not change
SECRET_KEY = "
abc
"
SECRET_KEY = "
something random
"
DEBUG = False
...
...
@@ -13,16 +13,44 @@ MAIL_PASSWORD = "password"
MAIL_PREFIX = "protokolle"
CELERY_BROKER_URL = "redis://localhost:6379/0"
CELERY_TASK_SERIALIZER = "pickle"
CELERY_ACCEPT_CONTENT = ["pickle"]
CELERY_TASK_SERIALIZER = "pickle"
# do not change
CELERY_ACCEPT_CONTENT = ["pickle"]
# do not change
URL_ROOT = "protokolle.example.com"
URL_PROTO = "https"
URL_PATH = "/"
URL_PARAMS = ""
LDAP_PROVIDER_URL = "ldaps://auth.example.com:389"
LDAP_BASE = "dc=example,dc=example,dc=com"
LDAP_PROTOCOL_VERSION = 3 # do not change
ETHERPAD_URL = "https://fachschaften.rwth-aachen.de/etherpad"
EMPTY_ETHERPAD = """Welcome to Etherpad!
This pad text is synchronized as you type, so that everyone viewing this page sees the same text. This allows you to collaborate seamlessly on documents!
Get involved with Etherpad at http://etherpad.org
""" # do not change
WIKI_ACTIVE = True
WIKI_API_URL = "https://wiki.example.com/wiki/api.php"
WIKI_ANONYMOUS = False
WIKI_USER = "user"
WIKI_PASSWORD = "password"
WIKI_DOMAIN = "domain" # set to None if not necessary
SESSION_PROTECTION = "strong"
SECURITY_KEY = "some other random string"
ERROR_CONTEXT_LINES = 3
PAGE_LENGTH = 20
PAGE_DIFF = 3
# choose something nice from fc-list
FONTS = {
"main": {
...
...
@@ -50,3 +78,8 @@ FONTS = {
"bolditalic": "Nimbus Mono PS"
}
}
DOCUMENTS_PATH = "documents"
PRIVATE_KEYWORDS = ["private", "internal", "privat", "intern"]
migrations/versions/d8c0c74b88bd_.py
0 → 100644
View file @
b3f71d25
"""empty message
Revision ID: d8c0c74b88bd
Revises: 495509e8f49a
Create Date: 2017-02-25 20:16:05.371638
"""
from
alembic
import
op
import
sqlalchemy
as
sa
# revision identifiers, used by Alembic.
revision
=
'd8c0c74b88bd'
down_revision
=
'495509e8f49a'
branch_labels
=
None
depends_on
=
None
def
upgrade
():
# ### commands auto generated by Alembic - please adjust! ###
op
.
add_column
(
'protocoltypes'
,
sa
.
Column
(
'use_wiki'
,
sa
.
Boolean
(),
nullable
=
True
))
op
.
add_column
(
'protocoltypes'
,
sa
.
Column
(
'wiki_category'
,
sa
.
String
(),
nullable
=
True
))
op
.
add_column
(
'protocoltypes'
,
sa
.
Column
(
'wiki_only_public'
,
sa
.
Boolean
(),
nullable
=
True
))
# ### end Alembic commands ###
def
downgrade
():
# ### commands auto generated by Alembic - please adjust! ###
op
.
drop_column
(
'protocoltypes'
,
'wiki_only_public'
)
op
.
drop_column
(
'protocoltypes'
,
'wiki_category'
)
op
.
drop_column
(
'protocoltypes'
,
'use_wiki'
)
# ### end Alembic commands ###
models/database.py
View file @
b3f71d25
...
...
@@ -26,6 +26,9 @@ class ProtocolType(db.Model):
public_group
=
db
.
Column
(
db
.
String
)
private_mail
=
db
.
Column
(
db
.
String
)
public_mail
=
db
.
Column
(
db
.
String
)
use_wiki
=
db
.
Column
(
db
.
Boolean
)
wiki_category
=
db
.
Column
(
db
.
String
)
wiki_only_public
=
db
.
Column
(
db
.
Boolean
)
protocols
=
relationship
(
"Protocol"
,
backref
=
backref
(
"protocoltype"
),
cascade
=
"all, delete-orphan"
,
order_by
=
"Protocol.id"
)
default_tops
=
relationship
(
"DefaultTOP"
,
backref
=
backref
(
"protocoltype"
),
cascade
=
"all, delete-orphan"
,
order_by
=
"DefaultTOP.number"
)
...
...
@@ -33,7 +36,8 @@ class ProtocolType(db.Model):
todos
=
relationship
(
"Todo"
,
backref
=
backref
(
"protocoltype"
),
order_by
=
"Todo.id"
)
def
__init__
(
self
,
name
,
short_name
,
organization
,
is_public
,
private_group
,
public_group
,
private_mail
,
public_mail
):
is_public
,
private_group
,
public_group
,
private_mail
,
public_mail
,
use_wiki
,
wiki_category
,
wiki_only_public
):
self
.
name
=
name
self
.
short_name
=
short_name
self
.
organization
=
organization
...
...
@@ -42,10 +46,19 @@ class ProtocolType(db.Model):
self
.
public_group
=
public_group
self
.
private_mail
=
private_mail
self
.
public_mail
=
public_mail
self
.
use_wiki
=
use_wiki
self
.
wiki_category
=
wiki_category
self
.
wiki_only_public
=
wiki_only_public
def
__repr__
(
self
):
return
"<ProtocolType(id={}, short_name={}, name={}, organization={}, is_public={}, private_group={}, public_group={})>"
.
format
(
self
.
id
,
self
.
short_name
,
self
.
name
,
self
.
organization
,
self
.
is_public
,
self
.
private_group
,
self
.
public_group
)
return
(
"<ProtocolType(id={}, short_name={}, name={}, "
"organization={}, is_public={}, private_group={}, "
"public_group={}, use_wiki={}, wiki_category='{}', "
"wiki_only_public={})>"
.
format
(
self
.
id
,
self
.
short_name
,
self
.
name
,
self
.
organization
,
self
.
is_public
,
self
.
private_group
,
self
.
public_group
,
self
.
use_wiki
,
self
.
wiki_category
,
self
.
wiki_only_public
))
def
get_latest_protocol
(
self
):
candidates
=
sorted
([
protocol
for
protocol
in
self
.
protocols
if
protocol
.
is_done
()],
key
=
lambda
p
:
p
.
date
,
reverse
=
True
)
...
...
@@ -153,6 +166,9 @@ class Protocol(db.Model):
self
.
protocoltype
.
short_name
.
lower
(),
self
.
date
.
strftime
(
"%y-%m-%d"
))
def
get_wiki_title
(
self
):
return
"Protokoll:{}-{:%Y-%m-%d}"
.
format
(
self
.
protocoltype
.
short_name
,
self
.
date
)
def
get_etherpad_link
(
self
):
identifier
=
self
.
get_identifier
()
if
identifier
is
None
:
...
...
@@ -305,8 +321,15 @@ class Todo(db.Model):
def
get_state
(
self
):
return
"[Erledigt]"
if
self
.
done
else
"[Offen]"
def
get_state_
tex
(
self
):
def
get_state_
plain
(
self
):
return
"Erledigt"
if
self
.
done
else
"Aktiv"
def
get_state_tex
(
self
):
return
self
.
get_state_plain
()
def
is_new
(
self
,
current_protocol
=
None
):
if
current_protocol
is
not
None
:
return
self
.
get_first_protocol
()
==
current_protocol
return
len
(
self
.
protocols
)
==
1
def
render_html
(
self
):
parts
=
[
...
...
@@ -317,16 +340,21 @@ class Todo(db.Model):
return
" "
.
join
(
parts
)
def
render_latex
(
self
,
current_protocol
=
None
):
is_new
=
len
(
self
.
protocols
)
==
1
if
current_protocol
is
not
None
:
is_new
=
self
.
get_first_protocol
()
==
current_protocol
return
r
"\textbf{{{}}}: {}: {} -- {}"
.
format
(
"Neuer Todo"
if
is_new
else
"Todo"
,
"Neuer Todo"
if
self
.
is_new
(
current_protocol
)
else
"Todo"
,
self
.
who
,
self
.
description
,
self
.
get_state_tex
()
)
def
render_wikitext
(
self
,
current_protocol
=
None
):
return
"'''{}:''' {}: {} - {}"
.
format
(
"Neuer Todo"
if
self
.
is_new
(
current_protocol
)
else
"Todo"
,
self
.
who
,
self
.
description
,
self
.
get_state_plain
()
)
class
TodoProtocolAssociation
(
db
.
Model
):
...
...
parser.py
View file @
b3f71d25
...
...
@@ -197,6 +197,12 @@ class Tag:
return
r
"\textbf{{{}:}} {}"
.
format
(
escape_tex
(
self
.
name
.
capitalize
()),
escape_tex
(
self
.
values
[
0
]))
elif
render_type
==
RenderType
.
plaintext
:
return
"{}: {}"
.
format
(
self
.
name
.
capitalize
(),
self
.
values
[
0
])
elif
render_type
==
RenderType
.
wikitext
:
if
self
.
name
==
"url"
:
return
"[{0} {0}]"
.
format
(
self
.
values
[
0
])
elif
self
.
name
==
"todo"
:
return
self
.
todo
.
render_wikitext
(
current_protocol
=
protocol
)
return
"'''{}:''' {}"
.
format
(
self
.
name
.
capitalize
(),
self
.
values
[
0
])
else
:
raise
_not_implemented
(
self
,
render_type
)
...
...
@@ -291,7 +297,7 @@ class Fork(Element):
def
test_private
(
self
,
name
):
stripped_name
=
name
.
replace
(
":"
,
""
).
strip
()
return
stripped_name
in
config
.
PRIVATE_KEYS
return
stripped_name
in
config
.
PRIVATE_KEY
WORD
S
def
render
(
self
,
render_type
,
show_private
,
level
,
protocol
=
None
):
name_line
=
self
.
name
if
self
.
name
is
not
None
and
len
(
self
.
name
)
>
0
else
""
...
...
@@ -317,14 +323,14 @@ class Fork(Element):
else
:
return
"
\n
"
.
join
([
name_line
,
begin_line
,
content_lines
,
end_line
])
elif
render_type
==
RenderType
.
wikitext
:
title_line
=
"{0}{1}{0}"
.
format
(
"="
*
(
level
+
2
),
name_line
)
title_line
=
"{0}
{1}
{0}"
.
format
(
"="
*
(
level
+
2
),
name_line
)
content_parts
=
[]
for
child
in
self
.
children
:
part
=
child
.
render
(
render_type
,
show_private
,
level
=
level
+
1
,
protocol
=
protocol
)
if
len
(
part
.
strip
())
==
0
:
continue
content_parts
.
append
(
part
)
content_lines
=
"{}
\n
{}
"
.
format
(
title_line
,
"
\n
"
.
join
(
content_parts
))
content_lines
=
"{}
\n
\n
{}
\n
"
.
format
(
title_line
,
"
\n
\n
"
.
join
(
content_parts
))
if
self
.
test_private
(
self
.
name
)
and
not
show_private
:
return
""
else
:
...
...
server.py
View file @
b3f71d25
...
...
@@ -88,7 +88,9 @@ def new_type():
protocoltype
=
ProtocolType
(
form
.
name
.
data
,
form
.
short_name
.
data
,
form
.
organization
.
data
,
form
.
is_public
.
data
,
form
.
private_group
.
data
,
form
.
public_group
.
data
,
form
.
private_mail
.
data
,
form
.
public_mail
.
data
)
form
.
private_mail
.
data
,
form
.
public_mail
.
data
,
form
.
use_wiki
.
data
,
form
.
wiki_category
.
data
,
form
.
wiki_only_public
.
data
)
db
.
session
.
add
(
protocoltype
)
db
.
session
.
commit
()
flash
(
"Der Protokolltyp {} wurde angelegt."
.
format
(
protocoltype
.
name
),
"alert-success"
)
...
...
tasks.py
View file @
b3f71d25
...
...
@@ -11,6 +11,7 @@ from server import celery, app
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
,
RenderType
from
wiki
import
WikiClient
,
WikiException
import
config
...
...
@@ -206,10 +207,28 @@ def parse_protocol_async(protocol_id, encoded_kwargs):
for
show_private
in
privacy_states
:
latex_source
=
texenv
.
get_template
(
"protocol.tex"
).
render
(
render_type
=
RenderType
.
latex
,
show_private
=
show_private
,
**
render_kwargs
)
compile
(
latex_source
,
protocol
,
show_private
=
show_private
)
# TODO render and push wiki
if
protocol
.
protocoltype
.
use_wiki
:
wiki_source
=
render_template
(
"protocol.wiki"
,
render_type
=
RenderType
.
wikitext
,
show_private
=
not
protocol
.
protocoltype
.
wiki_only_public
,
**
render_kwargs
).
replace
(
"
\n\n\n
"
,
"
\n\n
"
)
push_to_wiki
(
protocol
,
wiki_source
,
"Automatisch generiert vom Protokollsystem 3.0"
)
protocol
.
done
=
True
db
.
session
.
commit
()
def
push_to_wiki
(
protocol
,
content
,
summary
):
push_to_wiki_async
.
delay
(
protocol
.
id
,
content
,
summary
)
@
celery
.
task
def
push_to_wiki_async
(
protocol_id
,
content
,
summary
):
with
WikiClient
()
as
wiki_client
,
app
.
app_context
():
protocol
=
Protocol
.
query
.
filter_by
(
id
=
protocol_id
).
first
()
try
:
wiki_client
.
edit_page
(
title
=
protocol
.
get_wiki_title
(),
content
=
content
,
summary
=
"Automatisch generiert vom Protokollsystem 3."
)
except
WikiException
as
exc
:
error
=
protocol
.
create_error
(
"Pushing to Wiki"
,
"Pushing to Wiki failed."
,
str
(
exc
))
def
compile
(
content
,
protocol
,
show_private
):
compile_async
.
delay
(
content
,
protocol
.
id
,
show_private
)
...
...
templates/protocol.wiki
0 → 100644
View file @
b3f71d25
{{'{{'}}Infobox Protokoll
| name = {{protocol.protocoltype.name}}
| datum = {{protocol.date|datify}}
| zeit = von {{protocol.start_time|timify}} bis {{protocol.end_time|timify}}
| protokollant = {{protocol.author}}
| anwesende = {{protocol.participants}}
{{'}}'}}
== Beschlüsse ==
{% if protocol.decisions|length > 0 %}
{% for decision in protocol.decisions %}
* {{decision.content}}
{% endfor %}
{% else %}
* keine Beschlüsse
{% endif %}
{% for top in tree.children %}
{% if top|class == "Fork" %}
{{top.render(render_type=render_type, level=0, show_private=show_private, protocol=protocol)}}
{% endif %}
{% endfor %}
[[Kategorie:{{protocol.protocoltype.wiki_category}}]]
views/forms.py
View file @
b3f71d25
...
...
@@ -15,6 +15,9 @@ class ProtocolTypeForm(FlaskForm):
public_group
=
StringField
(
"Öffentliche Gruppe"
)
private_mail
=
StringField
(
"Interner Verteiler"
)
public_mail
=
StringField
(
"Öffentlicher Verteiler"
)
wiki_category
=
StringField
(
"Wiki-Kategorie"
)
use_wiki
=
BooleanField
(
"Wiki benutzen"
)
wiki_only_public
=
BooleanField
(
"Wiki ist öffentlich"
)
class
DefaultTopForm
(
FlaskForm
):
name
=
StringField
(
"Name"
,
validators
=
[
InputRequired
(
"Du musst einen Namen angeben."
)])
...
...
views/tables.py
View file @
b3f71d25
...
...
@@ -95,12 +95,16 @@ class ProtocolTypeTable(SingleValueTable):
super
().
__init__
(
protocoltype
.
name
,
protocoltype
,
newlink
=
url_for
(
"edit_type"
,
type_id
=
protocoltype
.
id
))
def
headers
(
self
):
return
[
"Name"
,
"Abkürzung"
,
"Organisation"
,
"Öffentlich"
,
headers
=
[
"Name"
,
"Abkürzung"
,
"Organisation"
,
"Öffentlich"
,
"Interne Gruppe"
,
"Öffentliche Gruppe"
,
"Interner Verteiler"
,
"Öffentlicher Verteiler"
]
"Interner Verteiler"
,
"Öffentlicher Verteiler"
,
"Wiki"
]
if
self
.
value
.
use_wiki
:
headers
.
append
(
"Wiki-Kategorie"
)
return
headers
def
row
(
self
):
r
eturn
[
r
ow
=
[
self
.
value
.
name
,
self
.
value
.
short_name
,
self
.
value
.
organization
,
...
...
@@ -108,8 +112,12 @@ class ProtocolTypeTable(SingleValueTable):
self
.
value
.
private_group
,
self
.
value
.
public_group
,
self
.
value
.
private_mail
,
self
.
value
.
public_mail
self
.
value
.
public_mail
,
Table
.
bool
(
self
.
value
.
use_wiki
)
+
(
", "
+
(
"Öffentlich"
if
self
.
value
.
wiki_only_public
else
"Intern"
))
if
self
.
value
.
use_wiki
else
""
]
if
self
.
value
.
use_wiki
:
row
.
append
(
self
.
value
.
wiki_category
)
return
row
class
DefaultTOPsTable
(
Table
):
def
__init__
(
self
,
tops
,
protocoltype
=
None
):
...
...
wiki.py
0 → 100644
View file @
b3f71d25
import
requests
import
json
import
config
HTTP_STATUS_OK
=
200
class
WikiException
(
Exception
):
pass
def
_filter_params
(
params
):
result
=
{}
for
key
,
value
in
sorted
(
params
.
items
(),
key
=
lambda
t
:
t
[
0
]
==
"token"
):
if
isinstance
(
value
,
bool
):
if
value
:
result
[
key
]
=
""
else
:
result
[
key
]
=
value
return
result
class
WikiClient
:
def
__init__
(
self
,
active
=
None
,
endpoint
=
None
,
anonymous
=
None
,
user
=
None
,
password
=
None
,
domain
=
None
):
self
.
active
=
active
if
active
is
not
None
else
config
.
WIKI_ACTIVE
self
.
endpoint
=
endpoint
if
endpoint
is
not
None
else
config
.
WIKI_API_URL
self
.
anonymous
=
anonymous
if
anonymous
is
not
None
else
config
.
WIKI_ANONYMOUS
self
.
user
=
user
if
user
is
not
None
else
config
.
WIKI_USER
self
.
password
=
password
if
password
is
not
None
else
config
.
WIKI_PASSWORD
self
.
domain
=
domain
if
domain
is
not
None
else
config
.
WIKI_DOMAIN
self
.
token
=
None
self
.
cookies
=
requests
.
cookies
.
RequestsCookieJar
()
def
__enter__
(
self
):
if
not
self
.
anonymous
:
self
.
login
()
return
self
def
__exit__
(
self
,
type
,
value
,
traceback
):
if
not
self
.
anonymous
:
self
.
logout
()
def
is_logged_in
(
self
):
return
self
.
token
is
not
None
def
login
(
self
):
if
not
self
.
active
:
return
# todo: Change this to the new MediaWiki tokens api once the wiki is updated
token_answer
=
self
.
do_action
(
"login"
,
method
=
"post"
,
lgname
=
self
.
user
)
if
"login"
not
in
token_answer
or
"token"
not
in
token_answer
[
"login"
]:
raise
WikiException
(
"No token in login answer."
)
lgtoken
=
token_answer
[
"login"
][
"token"
]
login_answer
=
self
.
do_action
(
"login"
,
method
=
"post"
,
lgname
=
self
.
user
,
lgpassword
=
self
.
password
,
lgdomain
=
self
.
domain
,
lgtoken
=
lgtoken
)
if
(
"login"
not
in
login_answer
or
"result"
not
in
login_answer
[
"login"
]
or
login_answer
[
"login"
][
"result"
]
!=
"Success"
):
raise
WikiException
(
"Login not successful."
)
def
logout
(
self
):
if
not
self
.
active
:
return
self
.
do_action
(
"logout"
)
def
edit_page
(
self
,
title
,
content
,
summary
,
recreate
=
True
,
createonly
=
False
):
if
not
self
.
active
:
return
# todo: port to new api once the wiki is updated
prop_answer
=
self
.
do_action
(
"query"
,
method
=
"get"
,
prop
=
"info"
,
intoken
=
"edit"
,
titles
=
title
)
if
(
"query"
not
in
prop_answer
or
"pages"
not
in
prop_answer
[
"query"
]):
raise
WikiException
(
"Can't get token for page {}"
.
format
(
title
))
pages
=
prop_answer
[
"query"
][
"pages"
]
edit_token
=
None
for
page
in
pages
.
values
():
if
page
[
"title"
]
==
title
:
edit_token
=
page
[
"edittoken"
]
break
if
edit_token
is
None
:
raise
WikiException
(
"Can't get token for page {}"
.
format
(
title
))
edit_answer
=
self
.
do_action
(
action
=
"edit"
,
method
=
"post"
,
data
=
{
"text"
:
content
},
token
=
edit_token
,
title
=
title
,
summary
=
summary
,
recreate
=
recreate
,
createonly
=
createonly
,
bot
=
True
)
def
do_action
(
self
,
action
,
method
=
"get"
,
data
=
None
,
**
kwargs
):
if
not
self
.
active
:
return
kwargs
[
"action"
]
=
action
kwargs
[
"format"
]
=
"json"
params
=
_filter_params
(
kwargs
)
req
=
None
if
method
==
"get"
:
req
=
requests
.
get
(
self
.
endpoint
,
cookies
=
self
.
cookies
,
params
=
params
)
elif
method
==
"post"
:
req
=
requests
.
post
(
self
.
endpoint
,
cookies
=
self
.
cookies
,
data
=
data
,
params
=
params
)
if
req
.
status_code
!=
HTTP_STATUS_OK
:
raise
WikiException
(
"HTTP status code {} on action {}."
.
format
(
req
.
status_code
,
action
))
self
.
cookies
=
req
.
cookies
return
req
.
json
()
def
main
():
with
WikiClient
()
as
client
:
client
.
edit_page
(
title
=
"Test"
,
content
=
"This is a very long text."
,
summary
=
"API client test"
)
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new 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