Completed
Push — master ( e187ac...8aa8f5 )
by Peter
07:52
created

Config.get()   C

Complexity

Conditions 14

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 20
rs 5.2747
cc 14

How to fix   Complexity   

Complexity

Complex classes like Config.get() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
import os
2
from configparser import SafeConfigParser
3
4
from django.core.exceptions import ImproperlyConfigured
5
6
script_dir = os.path.dirname(__file__)
7
VERSION = open(os.path.join(script_dir, 'VERSION')).read()
8
9
NOT_CONFIGURED_VALUE = '***not configured***'
10
11
12
class Config():
13
    config_file = None
14
    config = None
15
    is_production = None
16
17
    def __init__(self, config_files):
18
        '''
19
        Creates a new config object.
20
21
        Parameters:
22
        config_files: Dictionary with file_name: is_production setting
23
        '''
24
        for config_file, is_production in config_files:
25
            if os.path.isfile(config_file):
26
                self.config_file = config_file
27
                self.is_production = is_production
28
                self.config = SafeConfigParser()
29
                self.config.read([self.config_file], encoding='utf-8')
30
                return
31
32
        raise IOError("No configuration file found.")
33
34
    def has_option(self, name, category):
35
        return self.config.has_option(name, category)
36
37
    def get_bool(self, name, category, default):
38
        if not self.has_option(name, category):
39
            return default
40
        text = self.get(name, category)
41
        return text.lower() in ['true', 't', 'yes', 'active', 'enabled']
42
43
    def get(self, name, category, mandatory=False, expect_leading_slash=None, expect_trailing_slash=None):
44
        text = self.config.get(name, category)
45
        logtext = "Setting '[%s] %s' in %s has the value '%s'" % (category, name, self.config_file, text)
46
47
        if mandatory and text == NOT_CONFIGURED_VALUE:
48
            raise ImproperlyConfigured(logtext + ', but must be set.')
49
50
        if len(text) == 0:
51
            if expect_leading_slash or expect_trailing_slash:
52
                raise ImproperlyConfigured(logtext + ", but should not be empty.")
53
        else:
54
            if not text[0] == '/' and expect_leading_slash is True:
55
                raise ImproperlyConfigured(logtext + ", but should have a leading slash.")
56
            if not text[-1] == '/' and expect_trailing_slash is True:
57
                raise ImproperlyConfigured(logtext + ", but should have a trailing slash.")
58
            if text[0] == '/' and expect_leading_slash is False:
59
                raise ImproperlyConfigured(logtext + ", but shouldn't have a leading slash.")
60
            if text[-1] == '/' and expect_trailing_slash is False:
61
                raise ImproperlyConfigured(logtext + ", but shouldn't have a trailing slash.")
62
        return text
63
64
65
# Precedence rules are as follows:
66
# - Developer configuration overrides production configuration on developer machine.
67
# - Linux production system are more likely to happen than Windows developer machines.
68
config = Config((
69
    ('/etc/opensubmit/settings.ini', True),  # Linux production system
70
    (os.path.dirname(__file__) + '/settings_dev.ini', False),  # Linux / Mac development system
71
    (os.path.expandvars('$APPDATA') + 'opensubmit/settings.ini', False),  # Windows development system
72
    ))
73
74
################################################################################################################
75
################################################################################################################
76
################################################################################################################
77
78
# Global settings
79
DATABASES = {
80
    'default': {
81
        'ENGINE': 'django.db.backends.' + config.get('database', 'DATABASE_ENGINE'),
82
        'NAME': config.get('database', 'DATABASE_NAME', True),
83
        'USER': config.get('database', 'DATABASE_USER'),
84
        'PASSWORD': config.get('database', 'DATABASE_PASSWORD'),
85
        'HOST': config.get('database', 'DATABASE_HOST'),
86
        'PORT': config.get('database', 'DATABASE_PORT'),
87
    }
88
}
89
90
# We have the is_production indicator from above, which could also determine this value.
91
# But sometimes, you need Django stack traces in your production system for debugging.
92
DEBUG = config.get_bool('general', 'DEBUG', default=False)
93
94
# Demo mode allows login bypass
95
DEMO = config.get_bool('general', 'DEMO', default=False)
96
97
# Determine MAIN_URL / FORCE_SCRIPT option
98
HOST = config.get('server', 'HOST')
99
HOST_DIR = config.get('server', 'HOST_DIR')
100
if len(HOST_DIR) > 0:
101
    MAIN_URL = HOST + '/' + HOST_DIR
102
    FORCE_SCRIPT_NAME = '/' + HOST_DIR
103
else:
104
    MAIN_URL = HOST
105
    FORCE_SCRIPT_NAME = ''
106
107
# Determine some settings based on the MAIN_URL
108
LOGIN_URL = MAIN_URL
109
LOGIN_ERROR_URL = MAIN_URL
110
LOGIN_REDIRECT_URL = MAIN_URL + '/dashboard/'
111
112
# Local file system storage for uploads.
113
# Please note that MEDIA_URL is intentionally not set, since all media
114
# downloads have to use our download API URL for checking permissions.
115
MEDIA_ROOT = config.get('server', 'MEDIA_ROOT', True, True, True)
116
117
# Root of the installation
118
# This is normally detected automatically, so the settings.ini template does
119
# not contain the value. For the test suite, however, we need the override option.
120
if config.has_option('general', 'SCRIPT_ROOT'):
121
    SCRIPT_ROOT = config.get('general', 'SCRIPT_ROOT', True, True, False)
122
else:
123
    SCRIPT_ROOT = os.path.dirname(os.path.abspath(__file__))
124
125
# Determine list of allowed hosts
126
host_list = [MAIN_URL.split('/')[2]]
127
if ':' in host_list[0]:
128
    # Strip port number
129
    host_list = [host_list[0].split(':')[0]]
130
if config.has_option('server', 'HOST_ALIASES'):
131
    add_hosts = config.get('server', 'HOST_ALIASES', True, False, False).split(',')
132
    host_list += add_hosts
133
ALLOWED_HOSTS = host_list
134
135
if config.is_production:
136
    # Root folder for static files
137
    STATIC_ROOT = SCRIPT_ROOT + '/static-production/'
138
    STATICFILES_DIRS = (SCRIPT_ROOT + '/static/', )
139
    # Absolute URL for static files, directly served by Apache on production systems
140
    STATIC_URL = MAIN_URL + '/static/'
141
    EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
142
    SERVER_EMAIL = config.get('admin', 'ADMIN_EMAIL')
143
else:
144
    # Root folder for static files
145
    STATIC_ROOT = SCRIPT_ROOT + '/static/'
146
    # Relative URL for static files
147
    STATIC_URL = '/static/'
148
    EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
149
150
ADMINS = (
151
    (config.get('admin', 'ADMIN_NAME'), config.get('admin', 'ADMIN_EMAIL'), ),
152
)
153
MANAGERS = ADMINS
154
EMAIL_SUBJECT_PREFIX = '[OpenSubmit] '
155
TIME_ZONE = config.get("server", "TIME_ZONE")
156
LANGUAGE_CODE = 'en-en'
157
USE_I18N = True
158
USE_L10N = True
159
USE_TZ = False
160
STATICFILES_FINDERS = (
161
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
162
    'django.contrib.staticfiles.finders.FileSystemFinder',
163
)
164
SECRET_KEY = config.get("server", "SECRET_KEY")
165
166
TEMPLATES = [
167
    {
168
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
169
        'OPTIONS': {'debug': DEBUG,
170
                    'context_processors':
171
                        ("django.contrib.auth.context_processors.auth",
172
                         "django.template.context_processors.debug",
173
                         "django.template.context_processors.i18n",
174
                         "django.template.context_processors.media",
175
                         "django.template.context_processors.static",
176
                         "django.template.context_processors.tz",
177
                         "django.contrib.messages.context_processors.messages",
178
                         "opensubmit.contextprocessors.footer",
179
                         "django.template.context_processors.request",
180
                         "social_django.context_processors.backends",
181
                         "social_django.context_processors.login_redirect")
182
                    },
183
        'APP_DIRS': True,
184
    },
185
]
186
187
TEST_RUNNER = 'opensubmit.tests.DiscoverRunner'
188
189
MIDDLEWARE_CLASSES = (
190
    'django.middleware.common.CommonMiddleware',
191
    'django.contrib.sessions.middleware.SessionMiddleware',
192
    'django.middleware.csrf.CsrfViewMiddleware',
193
    'django.contrib.auth.middleware.AuthenticationMiddleware',
194
    'django.contrib.auth.middleware.RemoteUserMiddleware',
195
    'django.contrib.messages.middleware.MessageMiddleware',
196
    'social_django.middleware.SocialAuthExceptionMiddleware',
197
    'opensubmit.middleware.CourseRegister'
198
)
199
ROOT_URLCONF = 'opensubmit.urls'
200
WSGI_APPLICATION = 'opensubmit.wsgi.application'
201
INSTALLED_APPS = (
202
    'django.contrib.auth',
203
    'django.contrib.contenttypes',
204
    'django.contrib.sessions',
205
    'django.contrib.messages',
206
    'django.contrib.staticfiles',
207
    'formtools',
208
    'social_django',
209
    'bootstrapform',
210
    'grappelli.dashboard',
211
    'grappelli',
212
    'django.contrib.admin',
213
#    'django.contrib.admin.apps.SimpleAdminConfig',
214
    'opensubmit.app.OpenSubmitConfig',
215
)
216
217
LOG_FILE = config.get('server', 'LOG_FILE')
218
219
LOGGING = {
220
    'version': 1,
221
    'disable_existing_loggers': False,
222
    'filters': {
223
        'require_debug_false': {
224
            '()': 'django.utils.log.RequireDebugFalse'
225
        },
226
        'require_debug_true': {
227
            '()': 'django.utils.log.RequireDebugTrue'
228
        },
229
    },
230
    'formatters': {
231
        'verbose': {
232
            'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s"
233
        },
234
        'simple': {
235
            'format': '%(levelname)s %(message)s'
236
        },
237
    },
238
    'handlers': {
239
        'mail_admins': {
240
            'level': 'ERROR',
241
            'filters': ['require_debug_false'],
242
            'class': 'django.utils.log.AdminEmailHandler'
243
        },
244
        'console': {
245
            'level': 'DEBUG',
246
            'filters': ['require_debug_true'],
247
            'class': 'logging.StreamHandler'
248
        },
249
        'file': {
250
            'level': 'DEBUG',
251
            'class': 'logging.FileHandler',
252
            'formatter': 'verbose',
253
            'filename': LOG_FILE
254
        }
255
    },
256
    'loggers': {
257
        'django.request': {
258
            'handlers': ['mail_admins', 'file'],
259
            'level': 'ERROR',
260
            'propagate': True,
261
        },
262
        'OpenSubmit': {
263
            'handlers': ['console', 'file'],
264
            'level': 'DEBUG',
265
            'propagate': True,
266
        },
267
        'social': {
268
            'handlers': ['console', 'file'],
269
            'level': 'DEBUG',
270
            'propagate': True,
271
        },
272
    }
273
}
274
275
LOGIN_GOOGLE = (config.get("login", "LOGIN_GOOGLE_OAUTH_KEY").strip() != '' and
276
                config.get("login", "LOGIN_GOOGLE_OAUTH_SECRET").strip() != '')
277
LOGIN_GITHUB = (config.get("login", "LOGIN_GITHUB_OAUTH_KEY").strip() != '' and
278
                config.get("login", "LOGIN_GITHUB_OAUTH_SECRET").strip() != '')
279
LOGIN_TWITTER = (config.get("login", "LOGIN_TWITTER_OAUTH_KEY").strip() != '' and
280
                 config.get("login", "LOGIN_TWITTER_OAUTH_SECRET").strip() != '')
281
LOGIN_OPENID = (config.get('login', 'OPENID_PROVIDER').strip() != '')
282
LOGIN_SHIB = (config.get('login', 'LOGIN_SHIB_DESCRIPTION').strip() != '')
283
284
AUTHENTICATION_BACKENDS = (
285
    'django.contrib.auth.backends.ModelBackend',
286
)
287
288
if LOGIN_GOOGLE:
289
    AUTHENTICATION_BACKENDS += ('social_core.backends.google.GoogleOAuth2',)
290
    SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = config.get("login", "LOGIN_GOOGLE_OAUTH_KEY")
291
    SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = config.get("login", "LOGIN_GOOGLE_OAUTH_SECRET")
292
293
if LOGIN_TWITTER:
294
    AUTHENTICATION_BACKENDS += ('social_core.backends.twitter.TwitterOAuth',)
295
    SOCIAL_AUTH_TWITTER_KEY = config.get("login", "LOGIN_TWITTER_OAUTH_KEY")
296
    SOCIAL_AUTH_TWITTER_SECRET = config.get("login", "LOGIN_TWITTER_OAUTH_SECRET")
297
298
if LOGIN_GITHUB:
299
    AUTHENTICATION_BACKENDS += ('social_core.backends.github.GithubOAuth2',)
300
    SOCIAL_AUTH_GITHUB_KEY = config.get("login", "LOGIN_GITHUB_OAUTH_KEY")
301
    SOCIAL_AUTH_GITHUB_SECRET = config.get("login", "LOGIN_GITHUB_OAUTH_SECRET")
302
303
if LOGIN_OPENID:
304
    AUTHENTICATION_BACKENDS += ('opensubmit.social.open_id.OpenIdAuth',)
305
    LOGIN_DESCRIPTION = config.get('login', 'LOGIN_DESCRIPTION')
306
    OPENID_PROVIDER = config.get('login', 'OPENID_PROVIDER')
307
308
if LOGIN_SHIB:
309
    AUTHENTICATION_BACKENDS += ('opensubmit.social.apache.ModShibAuth',)
310
    LOGIN_SHIB_DESCRIPTION = config.get('login', 'LOGIN_SHIB_DESCRIPTION')
311
312
AUTHENTICATION_BACKENDS += ('opensubmit.social.lti.LtiAuth',)
313
314
if DEMO is True:
315
    AUTHENTICATION_BACKENDS += ('opensubmit.social.passthrough.PassThroughAuth',)
316
317
SOCIAL_AUTH_URL_NAMESPACE = 'social'
318
SOCIAL_AUTH_FIELDS_STORED_IN_SESSION = ['next', ]
319
SOCIAL_AUTH_PIPELINE = (
320
    'social_core.pipeline.social_auth.social_details',
321
    'social_core.pipeline.social_auth.social_uid',
322
    'social_core.pipeline.social_auth.auth_allowed',
323
    'social_core.pipeline.social_auth.social_user',
324
    'social_core.pipeline.user.get_username',
325
    'social_core.pipeline.social_auth.associate_by_email',  # Transition for existing users
326
    'social_core.pipeline.user.create_user',
327
    'social_core.pipeline.social_auth.associate_user',
328
    'social_core.pipeline.social_auth.load_extra_data',
329
    'social_core.pipeline.user.user_details',
330
    'opensubmit.views.demo.assign_role'
331
)
332
333
JOB_EXECUTOR_SECRET = config.get("executor", "SHARED_SECRET")
334
assert(JOB_EXECUTOR_SECRET is not "")
335
336
GRAPPELLI_ADMIN_TITLE = "OpenSubmit"
337
GRAPPELLI_SWITCH_USER = True
338
GRAPPELLI_INDEX_DASHBOARD = {
339
    'opensubmit.admin.teacher_backend': 'opensubmit.dashboard.TeacherDashboard'
340
}
341