Commit a0ca4d6d authored by Robin Sonnabend's avatar Robin Sonnabend
Browse files

Parse, check and generate the configuration

The configuration is defined in configproxy.py. Add new keys there.
Use ./configproxy.py check to check the config validity.
    ./configproxy.py create to create an example config.
parent 4d789866
...@@ -5,7 +5,7 @@ import quopri ...@@ -5,7 +5,7 @@ import quopri
from caldav import DAVClient from caldav import DAVClient
from vobject.base import ContentLine from vobject.base import ContentLine
import config from shared import config
class CalendarException(Exception): class CalendarException(Exception):
......
Subproject commit 4c0c16f069bb39d593b53eae0c2b92b1f05eba90 Subproject commit 2a0f881728f1000f27fb00321bc37fb869007fa9
# (local) database # [Database]
SQLALCHEMY_DATABASE_URI = "postgresql://user:password@host/database" # change this # Settings for SQLAlchemy
#SQLALCHEMY_DATABASE_URI = "mysql://user:password@host/database"
#SQLALCHEMY_DATABASE_URI = "sqlite:///path/to/database.db" # Database connection string. See
SQLALCHEMY_TRACK_MODIFICATIONS = False # do not change # http://docs.sqlalchemy.org/en/latest/core/engines.html for details. SQLite
# does not work with Alembic migrations.
SECRET_KEY = "something random" # change this SQLALCHEMY_DATABASE_URI = 'engine://user:password@host/database'
SERVER_NAME = "protokolle.example.com"
PREFERRED_URL_SCHEME = "https" # change to http for development
# [Security]
DEBUG = False # do not change # Secret keys and random strings
# mailserver (optional) # Secret random key used for session security.
MAIL_ACTIVE = True SECRET_KEY = b'\x86\xb1;\xfd\x8a\x05f\xf5/\xb22"\x07_\xc3\x021\x0f\xa2\xf6\xd56<i\xc8\xd4\xe0\xa4R\xf9D.\xa3B`\x8b#\xddYk\xfc\xcc\xe3\xe4\xce\xa1 \x81([\xc5e\xe7\xb0#\\x\x87\xa6-~\x15\x8c>\x19\xcc\xf1\x8f\xb0d\xc7\xdc\x0f\xcf\xe9\xc6\x03D\xfc~!7\xa8\xa9\x15\xe7\xa2\xde9\x18\x93N`\x0f\xdb\xb1\x0b\x08\xd6\xaa\xbb\x7f\x8f\xc5\x91\xf77\x07\x01\xce,\xcd\xfb\xfd\x1c\xd7N\xd3\x13\xbdR\xb8\x05\xd3\x98\xe8\xdcQ'
MAIL_FROM = "protokolle@example.com"
MAIL_HOST = "mail.example.com:465" # Secret random key used for user sessions.
MAIL_USER = "user" # set to "" for unauthenticated sending SECURITY_KEY = b"\xad\xf5\xfav\xbc\xb3u\xfc\xc0\xd3\xf4\xa1\x8bu\x81\x85\t%P\xba\xe5\xb8\xacv\x92\xa8\x16U]\xc5d\xdb\xf9\t\x95\xcc\x91\x88T\xedK]%\xd1\xe6gis\xf3N\xc1\xac\x98\x10q\xd1*\xcd\xce.\xec\xe4\x0f\x9b\xdd\xd1c\xa1\x01\xba\xdf\x0f%\x88\xdd\x03H\xd8\xc84F\xe6\xeb\xb1\x8a\x08\xd3<S?\xff\x9c\x19\xf4\xa5J\x84\xf4jy\xf1[\xf9\xe5\xce:>\xa9DS\xe9\xe3\x8f\x8d!\x8f\xa3\xf6p+\\\x86\xc9,:~E'"
MAIL_PASSWORD = "password" # set to "" for unauthenticated sending
MAIL_USE_TLS = True # should match the port in MAIL_HOST (if present there)
MAIL_USE_STARTTLS = False # Usually, it's either this or SMTPS, not both
# [URL Root]
# (local) message queue (necessary) # Where is the website hosted
CELERY_BROKER_URL = "redis://localhost:6379/0" # change this if you do not use redis or it is running somewhere else
CELERY_TASK_SERIALIZER = "pickle" # do not change # Domain on which this website is hosted
CELERY_ACCEPT_CONTENT = ["pickle"] # do not change SERVER_NAME = 'protokolle.example.com'
# Send exceptions to sentry (optional) # Protocol used by this website. Either 'http' or 'https'.
# SENTRY_DSN = "https://********:********@sentry.example.com//1" # PREFERRED_URL_SCHEME = 'https'
# CUPS printserver (optional)
PRINTING_ACTIVE = True # [Debug]
PRINTING_SERVER = "printsrv.example.com:631" # Debug mode. Do not set in production.
PRINTING_USER = "protocols"
PRINTING_PRINTERS = { # Activate debug mode
"example_printer": ["Duplex=DuplexNoTumble", "option2=value"], # DEBUG = False
"other_printer": ["list", "of", "options"]
}
# [Celery]
# etherpad (optional) # Settings for the task scheduler.
ETHERPAD_ACTIVE = True
ETHERPAD_URL = "https://example.com/etherpad" # without /p/… # Connection to the celery broker. See
EMPTY_ETHERPAD = """Welcome to Etherpad! # http://docs.celeryproject.org/en/latest/userguide/configuration.html
CELERY_BROKER_URL = 'redis://localhost:6379/0'
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 # [Sentry]
# Connection information for sentry exception reporting.
# wiki (optional)
WIKI_ACTIVE = True # Connection string for sentry. See
WIKI_TYPE = "MEDIAWIKI" # https://docs.sentry.io/quickstart/#configure-the-dsn
WIKI_API_URL = "https://wiki.example.com/wiki/api.php" # SENTRY_DSN = None
WIKI_ANONYMOUS = False
WIKI_USER = "user"
WIKI_PASSWORD = "password" from common import auth
WIKI_DOMAIN = "domain" # set to None if not necessary # [Authentication]
# User Authentication backend settings.
# CalDAV calendar (optional)
CALENDAR_ACTIVE = True # Time in seconds for which non-permanent sessions remain valid
CALENDAR_URL = "https://user:password@calendar.example.com/dav/" # AUTH_MAX_DURATION = 300
CALENDAR_DEFAULT_DURATION = 3 # default meeting length in hours
CALENDAR_MAX_REQUESTS = 10 # number of retries before giving up (some caldav servers like to randomly reply with errors) # Active Authentication backends
AUTH_BACKENDS = [StaticUserManager([('user', 'password', ('group',))])]
CALENDAR_TIMEZONE_MAP = {
"CET": "Europe/Berlin",
"CEST": "Europe/Berlin", # [Error Report Context]
} # Compiling error context settings
SESSION_PROTECTION = "strong" # do not change # Number of lines before and after an error to include in the error
# description
# authentication # ERROR_CONTEXT_LINES = 3
SECURITY_KEY = "some other random string" # change this
AUTH_MAX_DURATION = 300
from common.auth import LdapManager, ADManager, StaticUserManager # [Pagination]
AUTH_BACKENDS = [ # Pagination settings, used for list pages
LdapManager(
host="ldap.example.com", # Default number of entries per page
user_dn="uid={},ou=users,dc=example,dc=com", # PAGE_LENGTH = 20
group_dn="dc=example,dc=com"),
ADManager( # Number of pages before and after the current one with a direct link.
host="ad.example.com", # pass multiple hosts for replication # PAGE_DIFF = 3
domain="EXAMPLE",
user_dn="cn=users,dc=example,dc=com",
group_dn="dc=example,dc=com", # [Index Page]
ca_cert="/etc/ssl/certs/example-ca.pem"), # Settings for what to show on the index page
StaticUserManager(
users=( # Next days to list upcoming meetings on
("username", "password", ("group1", "group2")), # MAX_INDEX_DAYS = 14
("testuser", "abc123", ("group1",)),
) # Past days to list unfinished meetings on
), # MAX_PAST_INDEX_DAYS = 2
PAMManager(),
] # If a meeting is unfinished this many days after its date, a reminder is
# sent by mail
OBSOLETION_WARNING = """Please migrate your account!""" # not important # MAX_PAST_INDEX_DAYS_BEFORE_REMINDER = 14
# lines of error description
ERROR_CONTEXT_LINES = 3 # [Admin data]
# Settings for who is an admin and how to contact them
# pagination
PAGE_LENGTH = 20 # Mail address to tell users to contact in case of errors
PAGE_DIFF = 3 ADMIN_MAIL = 'admin@example.com'
# upcoming meetings within this number of days from today are shown on the index page # Users with this group are admins and are allowed to do and see everything.
MAX_INDEX_DAYS = 14 ADMIN_GROUP = 'admin'
MAX_PAST_INDEX_DAYS = 2
MAX_PAST_INDEX_DAYS_BEFORE_REMINDER = 14
# [Parser]
# mail to contact in case of complex errors # Settings for the protocol syntax parser
ADMIN_MAIL = "admin@example.com"
# users with this group may see and do everything # Do not enforce some parser policies.
ADMIN_GROUP = "admin" # PARSER_LAZY = False
# accept protocols even with some errors # Todos with at least this equality score are considered equal whe importing
# useful for importing old protocols # old protocols.
# not recommended for regular operation # FUZZY_MIN_SCORE = 90
PARSER_LAZY = False
# Keywords indicating private protocol parts
# minimum similarity (0-100) todos need to have to be considered equal while importing # PRIVATE_KEYWORDS = ['private', 'internal', 'privat', 'intern']
FUZZY_MIN_SCORE = 90
# choose something nice from fc-list # [Rendering]
# Nimbus Sans looks very much like Computer Modern # Settings for rendering protocols to pdf, html, etc.
FONTS = {
"main": { # fonts for xelatex
"extension": ".otf", FONTS = {'main': {'extension': '.otf', 'path': '/usr/share/fonts/gsfonts/', 'regular': 'NimbusSans-Regular', 'bold': 'NimbusSans-Bold', 'italic': 'NimbusSans-Italic', 'bolditalic': 'NimbusSans-BoldItalic'}, 'roman': {'extension': '.otf', 'path': '/usr/share/fonts/gsfonts/', 'regular': 'NimbusRoman-Regular', 'bold': 'NimbusRoman-Bold', 'italic': 'NimbusRoman-Italic', 'bolditalic': 'NimbusRoman-BoldItalic'}, 'sans': {'extension': '.otf', 'path': '/usr/share/fonts/gsfonts/', 'regular': 'NimbusSans-Regular', 'bold': 'NimbusSans-Bold', 'italic': 'NimbusSans-Italic', 'bolditalic': 'NimbusSans-BoldItalic'}, 'mono': {'extension': '.otf', 'path': '/usr/share/fonts/gsfonts/', 'regular': 'NimbusMonoPS-Regular', 'bold': 'NimbusMonoPS-Bold', 'italic': 'NimbusMonoPS-Italic', 'bolditalic': 'NimbusMonoPS-BoldItalic'}}
"path": "/usr/share/fonts/OTF/",
"regular": "NimbusSans-Regular", # Path to the directory to save protocols in. Write access is necessary.
"bold": "NimbusSans-Bold", # DOCUMENTS_PATH = 'documents'
"italic": "NimbusSans-Oblique",
"bolditalic": "NimbusSans-BoldOblique" # list of bulletpoints to use in latex
}, # LATEX_BULLETPOINTS = ['\\textbullet', '\\normalfont \\bfseries \\textendash', '\\textasteriskcentered', '\\textperiodcentered']
"roman": {
"extension": ".otf", # Header level at which to start with HTML headlines
"path": "/usr/share/fonts/OTF/", # HTML_LEVEL_OFFSET = 3
"regular": "NimbusRoman-Regular",
"bold": "NimbusRoman-Bold", # path to additional jinja2 templates
"italic": "NimbusRoman-Italic", # LATEX_LOCAL_TEMPLATES = None
"bolditalic": "NimbusRoman-BoldItalic"
}, # template to include at the top of protocols
"sans": { # LATEX_LOGO_TEMPLATE = None
"extension": ".otf",
"path": "/usr/share/fonts/OTF/", # custom latex page geometry
"regular": "NimbusSans-Regular", # LATEX_GEOMETRY = None
"bold": "NimbusSans-Bold",
"italic": "NimbusSans-Oblique", # custom latex pagestyle, e.g. 'fancy'
"bolditalic": "NimbusSans-BoldOblique" # LATEX_PAGESTYLE = None
},
"mono": { # Include a header and footer in protocols
"extension": ".otf", # LATEX_HEADER_FOOTER = False
"path": "/usr/share/fonts/OTF/",
"regular": "NimbusMonoPS-Regular", # define multiple LaTeX-templates to use with a each protocol type
"bold": "NimbusMonoPS-Bold", # individually overiding the general LATEX options the LATEX_LOCAL_TEMPLATES
"italic": "NimbusMonoPS-Italic", # parameter is need to provide the path for the templates each template must
"bolditalic": "NimbusMonoPS-BoldItalic" # be placed in an individual folder named by its ID in LATEX_TEMPLATES and
} # must contain the provided template files: e.g. the files for the template
} # 'yourtemplate' need to be in the folder named 'yourtemplate', and the
# templates provides the files: 'protokoll2.cls' (class), 'protocol.tex'
# local filesystem path to save compiled and uploaded protocols (and attachments) # (protocol), 'decision.tex' (decision) and 'asta-logo.tex'
# create this path! # LATEX_TEMPLATES = None
DOCUMENTS_PATH = "documents"
# keywords indicating private protocol parts # [MAIL] (optional)
PRIVATE_KEYWORDS = ["private", "internal", "privat", "intern"] # Mail server connection
# list of bulletpoints to use in latex # deactivate with
# these are latex-defaults, add more if you like more # MAIL_ACTIVE = False
# they are cycled as often as necessary to allow (theoretically) infinite nesting depth
LATEX_BULLETPOINTS = [ # Mail sender address
r"\textbullet", MAIL_FROM = 'protokolle@example.com'
r"\normalfont \bfseries \textendash",
r"\textasteriskcentered", # SMTP Mail server address with port
r"\textperiodcentered" MAIL_HOST = 'mail.example.com:465'
]
# Mail server login user. Empty for no authentication.
# optional: path to additional jinja-templates, will be need in combination with LATEX_TEMPLATES # MAIL_USER = ''
#LATEX_LOCAL_TEMPLATES = "local-templates"
# optional: the template to include at the top of protocol.tex # Mail server login password. Empty for no authentication.
#LATEX_LOGO_TEMPLATE = "asta-logo.tex" # MAIL_PASSWORD = ''
# optional: custom protocol page geometry
#LATEX_GEOMETRY = "bottom=1.6cm,top=1.6cm,inner=2.5cm,outer=1.0cm,footskip=1.0cm,headsep=0.6cm" # Use SMPTS (not STARTTLS). Should match port.
# optional: custom protocol pagestyle # MAIL_USE_TLS = True
#LATEX_PAGESTYLE = "fancy"
# optional: additional latex packages # Use SMTP with STARTTLS. Should match port.
#LATEX_ADDITIONAL_PACKAGES = ["[absolute]{textpos}", "{fancyheadings}"] # MAIL_USE_STARTTLS = False
# optional: include header and footer in asta-style, not just a page number on top
#LATEX_HEADER_FOOTER = True
# optional: define multiple LaTeX-templates to use with a each protocol type individually overiding the general LATEX options # [PRINTING] (optional)
# the LATEX_LOCAL_TEMPLATES parameter is need to provide the path for the templates # CUPS printing settings
# each template must be placed in an individual folder named by its ID in LATEX_TEMPLATES and must contain the provided template files: e.g.
# - the files for the template "yourtemplate" need to be in the folder named "yourtemplate" # deactivate with
# - the templates provides the files: "protokoll2.cls" (class), "protocol.tex" (protocol), "decision.tex" (decision) and "asta-logo.tex" # PRINTING_ACTIVE = False
#LATEX_TEMPLATES = {
# "yourtemplate": { # CUPS printserver connection
# "name": "Dein Template", PRINTING_SERVER = 'printsrv.example.com:631'
# "provides": ["class", "protocol", "decision"], # optional: if this option is set the corresponding files must be provided
# "logo": "asta-logo.tex", # optional: replaces the general template to include at the top of protocol.tex set by LATEX_LOGO_TEMPLATE # CUPS user for print jobs
# "geometry": "bottom=1.6cm,top=1.6cm,inner=2.5cm,outer=1.0cm,footskip=1.0cm,headsep=0.6cm", # optional: replaces the general protocol page geometry set by LATEX_GEOMETRY # PRINTING_USER = 'protocols'
# "pagestyle": "fancy", # optional: replaces the general protocol pagestyle set by LATEX_PAGESTYLE
# "additional_packages": ["[absolute]{textpos}", "{fancyheadings}"], # optional: replaces the general latex packages set by LATEX_ADDITIONAL_PACKAGES # printers with corresponding options
# "headerfooter": True # optional: replaces the general LATEX_HEADER_FOOTER option PRINTING_PRINTERS = {'example_printer': ['Duplex=DuplexNoTumble', 'option2=value'], 'other_printer': ['list', 'of', 'options']}
# }
#}
# [ETHERPAD] (optional)
HTML_LEVEL_OFFSET = 3 # Etherpad settings
def dummy_todomail_provider(): # deactivate with
return {"example": ("Name", "mail@example.com")} # ETHERPAD_ACTIVE = False
# if you want to generate this mapping automatically # URL of the etherpad installation. Do not include the '/p'!
# manually creating todomails through the web interface will still be possible for every authenticated user ETHERPAD_URL = 'https://example.com/etherpad'
# list of functions that return dicts mapping todomail-keys to a tuple containing name and mail address
ADDITIONAL_TODOMAIL_PROVIDERS = [ # The content a new etherpad contains.
dummy_todomail_provider # EMPTY_ETHERPAD = 'Welcome to Etherpad!\n\nThis 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!\n\nGet involved with Etherpad at http://etherpad.org\n\n'
]
# [WIKI] (optional)
# Mediawiki or Dokuwiki settings
# deactivate with
# WIKI_ACTIVE = False
# 'MEDIAWIKI' or 'DOKUWIKI'
WIKI_TYPE = 'MEDIAWIKI'
# API Endpoint for Mediawiki,
# 'https://user:password@wiki.example.com/lib/exe/xmlrpc.php' for Dokuwiki
WIKI_API_URL = 'https://wiki.example.com/wiki/api.php'
# Skip login (only for Mediawiki)
# WIKI_ANONYMOUS = False
# Login user (only for Mediawiki)
# WIKI_USER = None
# Login password (only for Mediawiki)
# WIKI_PASSWORD = None
# Login domain (only for Mediawiki)
# WIKI_DOMAIN = None
# [CALENDAR] (optional)
# CalDAV settings
# deactivate with
# CALENDAR_ACTIVE = False
# CalDAV server URL
CALENDAR_URL = 'https://user:password@calendar.example.com/dav/'
# Default meeting length in hours
# CALENDAR_DEFAULT_DURATION = 3
# Number of retries before giving a connection attempt up. Some CalDAV
# servers like to randomly reply with errors.
# CALENDAR_MAX_REQUESTS = 10
# Timezone abbreviation map. Add as needed.
# CALENDAR_TIMEZONE_MAP = {'CET': 'Europe/Berlin', 'CEST': 'Europe/Berline'}
This diff is collapsed.
...@@ -4,7 +4,7 @@ from fuzzywuzzy import process ...@@ -4,7 +4,7 @@ from fuzzywuzzy import process
from models.database import OldTodo, Protocol, ProtocolType, TodoMail from models.database import OldTodo, Protocol, ProtocolType, TodoMail
from shared import db from shared import db
import config from shared import config
def lookup_todo_id(old_candidates, new_who, new_description): def lookup_todo_id(old_candidates, new_who, new_description):
......
...@@ -7,7 +7,7 @@ from uuid import uuid4 ...@@ -7,7 +7,7 @@ from uuid import uuid4
from shared import ( from shared import (
db, date_filter_short, escape_tex, DATE_KEY, START_TIME_KEY, END_TIME_KEY, db, date_filter_short, escape_tex, DATE_KEY, START_TIME_KEY, END_TIME_KEY,
current_user) current_user, config)
from utils import get_etherpad_url, split_terms, check_ip_in_networks from utils import get_etherpad_url, split_terms, check_ip_in_networks
from models.errors import DateNotMatchingException from models.errors import DateNotMatchingException
from dateutil import tz from dateutil import tz
...@@ -17,7 +17,6 @@ import os ...@@ -17,7 +17,6 @@ import os
from sqlalchemy import event from sqlalchemy import event
from sqlalchemy.orm import relationship, backref from sqlalchemy.orm import relationship, backref
import config
from todostates import make_states from todostates import make_states
......
...@@ -6,7 +6,7 @@ from enum import Enum ...@@ -6,7 +6,7 @@ from enum import Enum
from shared import escape_tex from shared import escape_tex
from utils import footnote_hash from utils import footnote_hash
import config from shared import config
INDENT_LETTER = "-" INDENT_LETTER = "-"
......
...@@ -21,7 +21,6 @@ flake8==3.5.0 ...@@ -21,7 +21,6 @@ flake8==3.5.0
Flask==0.12.2 Flask==0.12.2
Flask-Migrate==2.1.1 Flask-Migrate==2.1.1
Flask-Script==2.0.6 Flask-Script==2.0.6
Flask-SocketIO==2.9.3
Flask-SQLAlchemy==2.3.2 Flask-SQLAlchemy==2.3.2
Flask-WTF==0.14.2 Flask-WTF==0.14.2
fuzzywuzzy==0.16.0 fuzzywuzzy==0.16.0
...@@ -60,6 +59,7 @@ regex==2018.2.8 ...@@ -60,6 +59,7 @@ regex==2018.2.8
requests==2.18.4 requests==2.18.4
six==1.11.0 six==1.11.0
SQLAlchemy==1.2.3 SQLAlchemy==1.2.3
SQLAlchemy-Utils==0.33.2
typing==3.6.4 typing==3.6.4
tzlocal==1.5.1 tzlocal==1.5.1
urllib3==1.22 urllib3==1.22
......
...@@ -21,9 +21,8 @@ from datetime import datetime, timedelta ...@@ -21,9 +21,8 @@ from datetime import datetime, timedelta
import math import math
import mimetypes import mimetypes
import config
from shared import ( from shared import (
db, date_filter, datetime_filter, date_filter_long, config, db, date_filter, datetime_filter, date_filter_long,
date_filter_short, time_filter, time_filter_short, user_manager, date_filter_short, time_filter, time_filter_short, user_manager,
security_manager, current_user, check_login, login_required, security_manager, current_user, check_login, login_required,
class_filter, needs_date_test, todostate_name_filter, class_filter, needs_date_test, todostate_name_filter,
...@@ -180,6 +179,19 @@ def merge_duplicate_todos(): ...@@ -180,6 +179,19 @@ def merge_duplicate_todos():
todo_by_id[todo_id] = todo todo_by_id[todo_id] = todo
@manager.command
def check_config():
# TODO: check how to set return codes
import configproxy
return configproxy.check_config()
@manager.command
def create_example_config(filename):
import configproxy
return configproxy.write_example_config(filename=filename)
@manager.command @manager.command
def runserver(): def runserver():
app.run() app.run()
......
...@@ -7,7 +7,12 @@ from enum import Enum ...@@ -7,7 +7,12 @@ from enum import Enum
from common import back from common import back
import config try:
import configproxy
config, public_config = configproxy.import_config()
except ImportError as error:
print(error)
raise
db = SQLAlchemy() db = SQLAlchemy()
......
...@@ -16,7 +16,7 @@ from models.errors import DateNotMatchingException ...@@ -16,7 +16,7 @@ from models.errors import DateNotMatchingException
from server import celery, app from server import celery, app
from shared import ( from shared import (
db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long, db, escape_tex, unhyphen, date_filter, datetime_filter, date_filter_long,
date_filter_short, time_filter, class_filter, KNOWN_KEYS, WikiType) date_filter_short, time_filter, class_filter, KNOWN_KEYS, WikiType, config)
from utils import ( from utils import (
mail_manager, add_line_numbers, mail_manager, add_line_numbers,
set_etherpad_text, parse_datetime_from_string) set_etherpad_text, parse_datetime_from_string)
...@@ -25,8 +25,6 @@ from wiki import WikiClient, WikiException ...@@ -25,8 +25,6 @@ from wiki import WikiClient, WikiException
from calendarpush import Client as CalendarClient, CalendarException from calendarpush import Client as CalendarClient, CalendarException
from legacy import lookup_todo_id from legacy import lookup_todo_id
import config
texenv = app.create_jinja_environment() texenv = app.create_jinja_environment()
texenv.block_start_string = r"\ENV{" texenv.block_start_string = r"\ENV{"
texenv.block_end_string = r"}" texenv.block_end_string = r"}"
......
...@@ -14,8 +14,9 @@ import ipaddress ...@@ -14,8 +14,9 @@ import ipaddress
from socket import getfqdn from socket import getfqdn
from uuid import uuid4 from uuid import uuid4
import subprocess import subprocess
import contextlib