cuhasc 0.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. cuhasc/__init__.py +0 -0
  2. cuhasc/admin.py +3 -0
  3. cuhasc/apps.py +5 -0
  4. cuhasc/base.py +18 -0
  5. cuhasc/cli.py +277 -0
  6. cuhasc/constants.py +11 -0
  7. cuhasc/cookies.py +110 -0
  8. cuhasc/data/handbook/Dummy-section1.md +11 -0
  9. cuhasc/data/handbook/README.md +3 -0
  10. cuhasc/data/instruments/cvscale-ar.tsv +27 -0
  11. cuhasc/data/instruments/cvscale-bg.tsv +27 -0
  12. cuhasc/data/instruments/cvscale-bn.tsv +27 -0
  13. cuhasc/data/instruments/cvscale-cs.tsv +27 -0
  14. cuhasc/data/instruments/cvscale-da.tsv +27 -0
  15. cuhasc/data/instruments/cvscale-de.tsv +27 -0
  16. cuhasc/data/instruments/cvscale-el.tsv +27 -0
  17. cuhasc/data/instruments/cvscale-en.tsv +27 -0
  18. cuhasc/data/instruments/cvscale-es.tsv +27 -0
  19. cuhasc/data/instruments/cvscale-et.tsv +27 -0
  20. cuhasc/data/instruments/cvscale-fa.tsv +27 -0
  21. cuhasc/data/instruments/cvscale-fi.tsv +27 -0
  22. cuhasc/data/instruments/cvscale-fr.tsv +27 -0
  23. cuhasc/data/instruments/cvscale-hi.tsv +27 -0
  24. cuhasc/data/instruments/cvscale-hr.tsv +27 -0
  25. cuhasc/data/instruments/cvscale-hu.tsv +27 -0
  26. cuhasc/data/instruments/cvscale-id.tsv +27 -0
  27. cuhasc/data/instruments/cvscale-it.tsv +27 -0
  28. cuhasc/data/instruments/cvscale-ja.tsv +27 -0
  29. cuhasc/data/instruments/cvscale-ko.tsv +27 -0
  30. cuhasc/data/instruments/cvscale-lt.tsv +27 -0
  31. cuhasc/data/instruments/cvscale-lv.tsv +27 -0
  32. cuhasc/data/instruments/cvscale-nl.tsv +27 -0
  33. cuhasc/data/instruments/cvscale-no.tsv +27 -0
  34. cuhasc/data/instruments/cvscale-pl.tsv +27 -0
  35. cuhasc/data/instruments/cvscale-pt.tsv +27 -0
  36. cuhasc/data/instruments/cvscale-ro.tsv +27 -0
  37. cuhasc/data/instruments/cvscale-ru.tsv +27 -0
  38. cuhasc/data/instruments/cvscale-sk.tsv +27 -0
  39. cuhasc/data/instruments/cvscale-sl.tsv +27 -0
  40. cuhasc/data/instruments/cvscale-sq.tsv +27 -0
  41. cuhasc/data/instruments/cvscale-sr.tsv +27 -0
  42. cuhasc/data/instruments/cvscale-sv.tsv +27 -0
  43. cuhasc/data/instruments/cvscale-ta.tsv +27 -0
  44. cuhasc/data/instruments/cvscale-th.tsv +27 -0
  45. cuhasc/data/instruments/cvscale-tr.tsv +27 -0
  46. cuhasc/data/instruments/cvscale-uk.tsv +27 -0
  47. cuhasc/data/instruments/cvscale-ur.tsv +27 -0
  48. cuhasc/data/instruments/cvscale-vi.tsv +27 -0
  49. cuhasc/data/instruments/cvscale-zh.tsv +27 -0
  50. cuhasc/data/instruments/dimensions-ar.csv +6 -0
  51. cuhasc/data/instruments/dimensions-bg.csv +6 -0
  52. cuhasc/data/instruments/dimensions-bn.csv +6 -0
  53. cuhasc/data/instruments/dimensions-cs.csv +6 -0
  54. cuhasc/data/instruments/dimensions-da.csv +6 -0
  55. cuhasc/data/instruments/dimensions-de.csv +6 -0
  56. cuhasc/data/instruments/dimensions-el.csv +6 -0
  57. cuhasc/data/instruments/dimensions-en.csv +6 -0
  58. cuhasc/data/instruments/dimensions-es.csv +6 -0
  59. cuhasc/data/instruments/dimensions-et.csv +6 -0
  60. cuhasc/data/instruments/dimensions-fa.csv +6 -0
  61. cuhasc/data/instruments/dimensions-fi.csv +6 -0
  62. cuhasc/data/instruments/dimensions-fr.csv +6 -0
  63. cuhasc/data/instruments/dimensions-hi.csv +6 -0
  64. cuhasc/data/instruments/dimensions-hr.csv +6 -0
  65. cuhasc/data/instruments/dimensions-hu.csv +6 -0
  66. cuhasc/data/instruments/dimensions-id.csv +6 -0
  67. cuhasc/data/instruments/dimensions-it.csv +6 -0
  68. cuhasc/data/instruments/dimensions-ja.csv +6 -0
  69. cuhasc/data/instruments/dimensions-ko.csv +6 -0
  70. cuhasc/data/instruments/dimensions-lt.csv +6 -0
  71. cuhasc/data/instruments/dimensions-lv.csv +6 -0
  72. cuhasc/data/instruments/dimensions-nl.csv +6 -0
  73. cuhasc/data/instruments/dimensions-no.csv +6 -0
  74. cuhasc/data/instruments/dimensions-pl.csv +6 -0
  75. cuhasc/data/instruments/dimensions-pt.csv +6 -0
  76. cuhasc/data/instruments/dimensions-ro.csv +6 -0
  77. cuhasc/data/instruments/dimensions-ru.csv +6 -0
  78. cuhasc/data/instruments/dimensions-sk.csv +6 -0
  79. cuhasc/data/instruments/dimensions-sl.csv +6 -0
  80. cuhasc/data/instruments/dimensions-sq.csv +6 -0
  81. cuhasc/data/instruments/dimensions-sr.csv +6 -0
  82. cuhasc/data/instruments/dimensions-sv.csv +6 -0
  83. cuhasc/data/instruments/dimensions-ta.csv +6 -0
  84. cuhasc/data/instruments/dimensions-th.csv +6 -0
  85. cuhasc/data/instruments/dimensions-tr.csv +6 -0
  86. cuhasc/data/instruments/dimensions-uk.csv +6 -0
  87. cuhasc/data/instruments/dimensions-ur.csv +6 -0
  88. cuhasc/data/instruments/dimensions-vi.csv +6 -0
  89. cuhasc/data/instruments/dimensions-zh.csv +6 -0
  90. cuhasc/data/instruments/scales-ar.csv +3 -0
  91. cuhasc/data/instruments/scales-bg.csv +3 -0
  92. cuhasc/data/instruments/scales-bn.csv +3 -0
  93. cuhasc/data/instruments/scales-cs.csv +3 -0
  94. cuhasc/data/instruments/scales-da.csv +3 -0
  95. cuhasc/data/instruments/scales-de.csv +3 -0
  96. cuhasc/data/instruments/scales-el.csv +3 -0
  97. cuhasc/data/instruments/scales-en.csv +3 -0
  98. cuhasc/data/instruments/scales-es.csv +3 -0
  99. cuhasc/data/instruments/scales-et.csv +3 -0
  100. cuhasc/data/instruments/scales-fa.csv +3 -0
  101. cuhasc/data/instruments/scales-fi.csv +3 -0
  102. cuhasc/data/instruments/scales-fr.csv +3 -0
  103. cuhasc/data/instruments/scales-hi.csv +3 -0
  104. cuhasc/data/instruments/scales-hr.csv +3 -0
  105. cuhasc/data/instruments/scales-hu.csv +3 -0
  106. cuhasc/data/instruments/scales-id.csv +3 -0
  107. cuhasc/data/instruments/scales-it.csv +3 -0
  108. cuhasc/data/instruments/scales-ja.csv +3 -0
  109. cuhasc/data/instruments/scales-ko.csv +3 -0
  110. cuhasc/data/instruments/scales-lt.csv +3 -0
  111. cuhasc/data/instruments/scales-lv.csv +3 -0
  112. cuhasc/data/instruments/scales-nl.csv +3 -0
  113. cuhasc/data/instruments/scales-no.csv +3 -0
  114. cuhasc/data/instruments/scales-pl.csv +3 -0
  115. cuhasc/data/instruments/scales-pt.csv +3 -0
  116. cuhasc/data/instruments/scales-ro.csv +3 -0
  117. cuhasc/data/instruments/scales-ru.csv +3 -0
  118. cuhasc/data/instruments/scales-sk.csv +3 -0
  119. cuhasc/data/instruments/scales-sl.csv +3 -0
  120. cuhasc/data/instruments/scales-sq.csv +3 -0
  121. cuhasc/data/instruments/scales-sr.csv +3 -0
  122. cuhasc/data/instruments/scales-sv.csv +3 -0
  123. cuhasc/data/instruments/scales-ta.csv +3 -0
  124. cuhasc/data/instruments/scales-th.csv +3 -0
  125. cuhasc/data/instruments/scales-tr.csv +3 -0
  126. cuhasc/data/instruments/scales-uk.csv +3 -0
  127. cuhasc/data/instruments/scales-ur.csv +3 -0
  128. cuhasc/data/instruments/scales-vi.csv +3 -0
  129. cuhasc/data/instruments/scales-zh.csv +3 -0
  130. cuhasc/deployment.py +105 -0
  131. cuhasc/forms.py +39 -0
  132. cuhasc/handbook.py +193 -0
  133. cuhasc/i18n.py +1153 -0
  134. cuhasc/instruments.py +111 -0
  135. cuhasc/management/__init__.py +0 -0
  136. cuhasc/management/commands/__init__.py +0 -0
  137. cuhasc/management/commands/adminpage.py +31 -0
  138. cuhasc/migrations/0001_initial.py +22 -0
  139. cuhasc/migrations/0002_team_member_token.py +19 -0
  140. cuhasc/migrations/0003_member.py +23 -0
  141. cuhasc/migrations/0004_qresult.py +26 -0
  142. cuhasc/migrations/0005_adminpage.py +20 -0
  143. cuhasc/migrations/0006_member_order_seed.py +19 -0
  144. cuhasc/migrations/__init__.py +0 -0
  145. cuhasc/models.py +104 -0
  146. cuhasc/plots.py +103 -0
  147. cuhasc/settings.py +113 -0
  148. cuhasc/static/cuhasc/cuhasc-logo.svg +9 -0
  149. cuhasc/static/cuhasc/cuhasc.css +104 -0
  150. cuhasc/static/cuhasc/favicon.png +0 -0
  151. cuhasc/static/cuhasc/vendor/bootstrap/bootstrap.bundle.min.js +7 -0
  152. cuhasc/static/cuhasc/vendor/bootstrap/bootstrap.min.css +6 -0
  153. cuhasc/templates/cuhasc/_language_selector.html +23 -0
  154. cuhasc/templates/cuhasc/adminpage.html +24 -0
  155. cuhasc/templates/cuhasc/base.html +28 -0
  156. cuhasc/templates/cuhasc/create_member.html +35 -0
  157. cuhasc/templates/cuhasc/create_team.html +12 -0
  158. cuhasc/templates/cuhasc/edit_member.html +34 -0
  159. cuhasc/templates/cuhasc/edit_team.html +12 -0
  160. cuhasc/templates/cuhasc/handbook_section.html +8 -0
  161. cuhasc/templates/cuhasc/home.html +45 -0
  162. cuhasc/templates/cuhasc/show_member.html +16 -0
  163. cuhasc/templates/cuhasc/show_team.html +47 -0
  164. cuhasc/test/__init__.py +0 -0
  165. cuhasc/test/test_base.py +18 -0
  166. cuhasc/test/test_cli.py +161 -0
  167. cuhasc/test/test_deployment.py +77 -0
  168. cuhasc/test/test_handbook.py +240 -0
  169. cuhasc/test/test_handbook_views.py +61 -0
  170. cuhasc/test/test_i18n.py +21 -0
  171. cuhasc/test/test_instruments.py +57 -0
  172. cuhasc/test/test_models.py +128 -0
  173. cuhasc/test/test_settings.py +18 -0
  174. cuhasc/test/test_views.py +295 -0
  175. cuhasc/urls.py +15 -0
  176. cuhasc/views.py +304 -0
  177. cuhasc/wsgi.py +16 -0
  178. cuhasc-0.6.0.dist-info/METADATA +241 -0
  179. cuhasc-0.6.0.dist-info/RECORD +182 -0
  180. cuhasc-0.6.0.dist-info/WHEEL +4 -0
  181. cuhasc-0.6.0.dist-info/entry_points.txt +2 -0
  182. cuhasc-0.6.0.dist-info/licenses/LICENSE +21 -0
cuhasc/__init__.py ADDED
File without changes
cuhasc/admin.py ADDED
@@ -0,0 +1,3 @@
1
+ from django.contrib import admin
2
+
3
+ # Register your models here.
cuhasc/apps.py ADDED
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class CuhascConfig(AppConfig):
5
+ name = 'cuhasc'
cuhasc/base.py ADDED
@@ -0,0 +1,18 @@
1
+ """elementary, domain-independent helper functions"""
2
+
3
+ import secrets
4
+
5
+
6
+ def join_url(base_url: str, path: str) -> str:
7
+ """Absolute URL from a base URL, with or without trailing slash, and a root-relative path."""
8
+ return f"{base_url.rstrip('/')}{path}"
9
+
10
+
11
+ def random_token(length: int) -> str:
12
+ """alphanumeric string starting with a non-hex digit character."""
13
+ alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"
14
+ shortalphabet = "ghijklmnopqrstuvwxyz"
15
+ result = [secrets.choice(shortalphabet)]
16
+ for _ in range(length-1):
17
+ result.append(secrets.choice(alphabet))
18
+ return "".join(result)
cuhasc/cli.py ADDED
@@ -0,0 +1,277 @@
1
+ """
2
+ The ``cuhasc`` command: the entire user interface of an installed CuHaSc.
3
+
4
+ A Culture Lead is not expected to be a developer, so this command must cover everything an
5
+ installed deployment needs -- starting the server, recovering the admin link, backing the data
6
+ up -- without a source checkout, a ``manage.py`` or a Django vocabulary. ``cuhasc manage`` is
7
+ the escape hatch for the cases that were not foreseen.
8
+
9
+ Ordering matters in ``main()``: the settings read their configuration from the environment at
10
+ import time and refuse to start without a secret key, so all environment variables must be set
11
+ and the data directory must exist *before* ``django.setup()`` is called.
12
+ """
13
+ import argparse
14
+ import contextlib
15
+ import datetime
16
+ import importlib.metadata
17
+ import os
18
+ import pathlib
19
+ import secrets
20
+ import socket
21
+ import sqlite3
22
+ import sys
23
+
24
+ import cuhasc.deployment as deployment
25
+
26
+ DEFAULT_HOST = '0.0.0.0' # reachable from the LAN; deployments behind a tunnel use 127.0.0.1
27
+ DEFAULT_PORT = 8037
28
+ SERVER_THREADS = 4 # waitress' own default; ample for SQLite and sub-100ms requests
29
+ SECRET_KEY_LENGTH = 50 # bytes of randomness; Django's own checks want a long key
30
+ WILDCARD_HOSTS = ('0.0.0.0', '::', '') # bind addresses that are not usable in a link
31
+
32
+
33
+ def main(argv: list[str] | None = None) -> int:
34
+ args = _parser().parse_args(argv)
35
+ if args.data_dir:
36
+ os.environ[deployment.DATA_DIR_ENV] = str(args.data_dir)
37
+ if getattr(args, 'public_url', None):
38
+ os.environ[deployment.PUBLIC_URL_ENV] = args.public_url.rstrip('/')
39
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'cuhasc.settings')
40
+ _create_data_dir()
41
+ import django
42
+ django.setup()
43
+ return args.execute(args)
44
+
45
+
46
+ def _adminpage(args: argparse.Namespace) -> int:
47
+ from django.core.management import call_command
48
+ call_command('adminpage', base_url=_base_url('localhost', args.port))
49
+ return 0
50
+
51
+
52
+ def _backup(args: argparse.Namespace) -> int:
53
+ source = _database_path()
54
+ if not source.is_file():
55
+ print(f"error: there is no database at {source} yet", file=sys.stderr)
56
+ return 1
57
+ target = args.file or pathlib.Path(f"cuhasc-backup-{datetime.date.today().isoformat()}.sqlite3")
58
+ if target.exists() and not args.force:
59
+ print(f"error: {target} exists; use --force to overwrite it", file=sys.stderr)
60
+ return 1
61
+ _copy_database(source, target)
62
+ print(f"wrote {target} ({target.stat().st_size} bytes)")
63
+ return 0
64
+
65
+
66
+ def _base_url(host: str, port: int) -> str:
67
+ """The URL a browser should use: the public one if configured, else the bind address --
68
+ with wildcard bind addresses replaced, since '0.0.0.0' means 'all of them', not a host."""
69
+ public_url = deployment.public_url()
70
+ if public_url:
71
+ return public_url
72
+ return f"http://{'localhost' if host in WILDCARD_HOSTS else host}:{port}"
73
+
74
+
75
+ def _copy_database(source: pathlib.Path, target: pathlib.Path):
76
+ """Copy via SQLite's own backup API, which is safe while the server is running -- unlike
77
+ a plain file copy, which can catch the database mid-write."""
78
+ with contextlib.closing(sqlite3.connect(f"file:{source}?mode=ro", uri=True)) as origin:
79
+ with contextlib.closing(sqlite3.connect(target)) as copy:
80
+ origin.backup(copy)
81
+
82
+
83
+ def _database_path() -> pathlib.Path:
84
+ from django.conf import settings
85
+ return pathlib.Path(settings.DATABASES['default']['NAME'])
86
+
87
+
88
+ def _create_data_dir() -> pathlib.Path:
89
+ """Create the data directory and, in an installed deployment, its secret key.
90
+
91
+ The settings deliberately never write, so this is the one place that does. In a source
92
+ checkout no key file is written: the well-known development key applies there, and a
93
+ checkout should not accumulate deployment state.
94
+ """
95
+ data_dir = deployment.data_dir()
96
+ data_dir.mkdir(parents=True, exist_ok=True)
97
+ keyfile = data_dir / deployment.SECRET_KEY_FILE
98
+ if not keyfile.is_file() and not deployment.is_source_checkout():
99
+ keyfile.write_text(f"{secrets.token_urlsafe(SECRET_KEY_LENGTH)}\n", encoding='utf-8')
100
+ keyfile.chmod(0o600)
101
+ return data_dir
102
+
103
+
104
+ def _info(args: argparse.Namespace) -> int:
105
+ from django.conf import settings
106
+ import cuhasc.handbook as handbook
107
+ import cuhasc.instruments as instruments
108
+ database = _database_path()
109
+ sections = sum(len(chapter) for chapter in handbook.get_sections_by_chapter().values())
110
+ print(f"cuhasc {_version()}")
111
+ print(f" data directory: {settings.DATA_DIR}")
112
+ print(f" database: {database}{'' if database.is_file() else ' (not created yet)'}")
113
+ print(f" questionnaire: {_plural(len(instruments.get_languages()), 'language')}")
114
+ print(f" handbook: {_plural(sections, 'section')}")
115
+ print(f" content: {_stored_content(database)}")
116
+ for label, url in _urls('localhost', args.port).items():
117
+ print(f" {label + ':':15} {url}")
118
+ return 0
119
+
120
+
121
+ def _lan_url(host: str, port: int) -> str | None:
122
+ """The URL to hand to Team Members on the same LAN, or None when it is not applicable.
123
+
124
+ The UDP socket selects a route; it sends nothing and needs no reachable network.
125
+ """
126
+ if host not in WILDCARD_HOSTS or deployment.public_url():
127
+ return None
128
+ with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as probe:
129
+ try:
130
+ probe.connect(('10.255.255.255', 1))
131
+ return f"http://{probe.getsockname()[0]}:{port}"
132
+ except OSError:
133
+ return None
134
+
135
+
136
+ def _manage(args: argparse.Namespace) -> int:
137
+ from django.core.management import execute_from_command_line
138
+ execute_from_command_line(['cuhasc manage', *args.rest])
139
+ return 0
140
+
141
+
142
+ def _parser() -> argparse.ArgumentParser:
143
+ parser = argparse.ArgumentParser(prog='cuhasc', description="Culture Handbook for Scrum.")
144
+ parser.add_argument('--version', action='version', version=f"cuhasc {_version()}")
145
+ common = argparse.ArgumentParser(add_help=False)
146
+ common.add_argument('--data-dir', type=pathlib.Path, default=None,
147
+ help=f"where database and secret key live "
148
+ f"(default: ${deployment.DATA_DIR_ENV} or ~/{deployment.DATA_DIR_NAME})")
149
+ subparsers = parser.add_subparsers(dest='command', required=True)
150
+
151
+ run = subparsers.add_parser('run', parents=[common], help="start the server")
152
+ run.add_argument('--host', default=DEFAULT_HOST,
153
+ help=f"address to bind to (default: {DEFAULT_HOST}, i.e. reachable from "
154
+ f"the LAN; use 127.0.0.1 when only a tunnel should reach it)")
155
+ run.add_argument('--port', type=int, default=DEFAULT_PORT, help=f"default: {DEFAULT_PORT}")
156
+ run.add_argument('--public-url', default=None,
157
+ help="URL Team Members will use, if a tunnel or proxy makes it differ from "
158
+ "the bind address; without it, form submissions behind a tunnel fail")
159
+ run.set_defaults(execute=_run)
160
+
161
+ adminpage = subparsers.add_parser('adminpage', parents=[common],
162
+ help="reset the admin-page token and print its link")
163
+ adminpage.add_argument('--port', type=int, default=DEFAULT_PORT)
164
+ adminpage.add_argument('--public-url', default=None)
165
+ adminpage.set_defaults(execute=_adminpage)
166
+
167
+ info = subparsers.add_parser('info', parents=[common],
168
+ help="show version, data directory and the URLs to hand out")
169
+ info.add_argument('--port', type=int, default=DEFAULT_PORT)
170
+ info.add_argument('--public-url', default=None)
171
+ info.set_defaults(execute=_info)
172
+
173
+ backup = subparsers.add_parser('backup', parents=[common], help="copy the database to a file")
174
+ backup.add_argument('file', nargs='?', type=pathlib.Path, default=None,
175
+ help="default: cuhasc-backup-<today>.sqlite3 in the current directory")
176
+ backup.add_argument('--force', action='store_true', help="overwrite an existing file")
177
+ backup.set_defaults(execute=_backup)
178
+
179
+ restore = subparsers.add_parser('restore', parents=[common],
180
+ help="replace the database with a backup")
181
+ restore.add_argument('file', type=pathlib.Path)
182
+ restore.add_argument('--force', action='store_true',
183
+ help="required once a database exists; the old one is kept alongside")
184
+ restore.set_defaults(execute=_restore)
185
+
186
+ manage = subparsers.add_parser('manage', parents=[common],
187
+ help="run a Django management command (escape hatch)")
188
+ manage.add_argument('rest', nargs=argparse.REMAINDER)
189
+ manage.set_defaults(execute=_manage)
190
+ return parser
191
+
192
+
193
+ def _restore(args: argparse.Namespace) -> int:
194
+ if not args.file.is_file():
195
+ print(f"error: no such backup file: {args.file}", file=sys.stderr)
196
+ return 1
197
+ target = _database_path()
198
+ if target.is_file():
199
+ if not args.force:
200
+ print(f"error: {target} exists; use --force to replace it", file=sys.stderr)
201
+ return 1
202
+ superseded = target.with_suffix(f".{datetime.datetime.now():%Y%m%d-%H%M%S}.sqlite3")
203
+ _copy_database(target, superseded)
204
+ print(f"kept the previous database as {superseded}")
205
+ _copy_database(args.file, target)
206
+ print(f"restored {args.file} into {target}")
207
+ return 0
208
+
209
+
210
+ def _run(args: argparse.Namespace) -> int:
211
+ import waitress
212
+ from django.core.management import call_command
213
+ import cuhasc.instruments as instruments
214
+ from cuhasc.models import AdminPage
215
+ from cuhasc.wsgi import application
216
+ call_command('migrate', verbosity=0, interactive=False) # idempotent, so there is no setup step
217
+ if not instruments.get_languages():
218
+ # Turn a silent failure into a loud one: the loaders glob, so a data directory that is
219
+ # missing yields no questionnaire at all rather than an error, and the deployment would
220
+ # look healthy until the first Team Member opened the form.
221
+ print(f"error: no questionnaire data in {instruments.INSTRUMENTS_DIR}", file=sys.stderr)
222
+ return 1
223
+ adminpage = AdminPage.current()
224
+ base_url = _base_url(args.host, args.port)
225
+ _print_banner(args, adminpage.token)
226
+ waitress.serve(application, host=args.host, port=args.port, threads=SERVER_THREADS,
227
+ # Waitress strips X-Forwarded-Proto from untrusted peers by default, so a
228
+ # proxy header cannot tell Django that the tunnel terminated TLS. Stating the
229
+ # scheme here trusts no client input and needs no proxy address.
230
+ url_scheme='https' if base_url.startswith('https://') else 'http')
231
+ return 0
232
+
233
+
234
+ def _plural(count: int, noun: str) -> str:
235
+ return f"{count} {noun}{'' if count == 1 else 's'}"
236
+
237
+
238
+ def _print_banner(args: argparse.Namespace, adminpage_token: str):
239
+ from django.conf import settings
240
+ print(f"CuHaSc {_version()}")
241
+ print(f" data directory: {settings.DATA_DIR}")
242
+ for label, url in _urls(args.host, args.port, adminpage_token).items():
243
+ print(f" {label + ':':15} {url}")
244
+ print("Press Ctrl-C to stop.")
245
+ sys.stdout.flush() # stdout is block-buffered off a terminal, and waitress then never returns
246
+
247
+
248
+ def _stored_content(database: pathlib.Path) -> str:
249
+ """How much data the deployment holds. Checks the file first, because merely querying
250
+ would make Django create an empty database -- `cuhasc info` must not change anything."""
251
+ from django.db import OperationalError
252
+ from cuhasc.models import Member, Team
253
+ if not database.is_file():
254
+ return "none yet; `cuhasc run` creates the database"
255
+ try:
256
+ return f"{_plural(Team.objects.count(), 'team')}, {_plural(Member.objects.count(), 'member')}"
257
+ except OperationalError:
258
+ return "unreadable; the database exists but has no tables"
259
+
260
+
261
+ def _urls(host: str, port: int, adminpage_token: str = '') -> dict[str, str]:
262
+ import cuhasc.base as base
263
+ base_url = _base_url(host, port)
264
+ urls = {'home page': base_url}
265
+ lan_url = _lan_url(host, port)
266
+ if lan_url:
267
+ urls['on this LAN'] = lan_url
268
+ if adminpage_token:
269
+ urls['admin page'] = base.join_url(base_url, f"/adminpage/{adminpage_token}")
270
+ return urls
271
+
272
+
273
+ def _version() -> str:
274
+ try:
275
+ return importlib.metadata.version('cuhasc')
276
+ except importlib.metadata.PackageNotFoundError:
277
+ return "(source checkout)"
cuhasc/constants.py ADDED
@@ -0,0 +1,11 @@
1
+ import pathlib
2
+
3
+ TOKEN_LENGTH_TEAM = 11
4
+ TOKEN_LENGTH_MEMBER = 10
5
+ TOKEN_LENGTH_ADMINPAGE = 20
6
+ ORDER_SEED_LENGTH = 8 # length of the (non-secret) seed used to randomize question order
7
+ COOKIE_NAME = 'cuhasc'
8
+ HIGH_CUTOFF: float = 4.0 # a Score at or above this is "high"
9
+ LOW_CUTOFF: float = 2.0 # a Score at or below this is "low"
10
+ HANDBOOK_DIR = pathlib.Path(__file__).resolve().parent / 'data' / 'handbook'
11
+ IMAGE_POOL_DIR = HANDBOOK_DIR / 'img'
cuhasc/cookies.py ADDED
@@ -0,0 +1,110 @@
1
+ from dataclasses import dataclass, asdict
2
+ import json
3
+
4
+ from django.urls import reverse
5
+
6
+ from cuhasc.models import Member, Team
7
+ import cuhasc.constants as c
8
+
9
+
10
+ @dataclass
11
+ class Role:
12
+ """Cookie representation of one Team or Member."""
13
+ classname: str
14
+ id: int
15
+ token: str
16
+
17
+
18
+ class CuhascCookie:
19
+ """Reads and writes the cuhasc browser cookie.
20
+
21
+ Usage:
22
+ cookie = CuhascCookie(request)
23
+ cookie.add(team)
24
+ response.set_cookie(COOKIE_NAME, cookie.cookietext)
25
+ """
26
+
27
+ def __init__(self, request):
28
+ self._request = request
29
+ self._roles: dict[tuple[str, int], Role] = {}
30
+ self._language: str | None = None
31
+ self._parse(request.COOKIES.get(c.COOKIE_NAME, ''))
32
+
33
+ def _parse(self, text):
34
+ if not text:
35
+ return
36
+ try:
37
+ data = json.loads(text)
38
+ except (json.JSONDecodeError, TypeError):
39
+ return
40
+ for entry in data.get('roles', []):
41
+ try:
42
+ role = Role(classname=entry['classname'], id=int(entry['id']), token=entry['token'])
43
+ self._roles[(role.classname, role.id)] = role
44
+ except (KeyError, ValueError):
45
+ pass
46
+ lang = data.get('language')
47
+ if isinstance(lang, str):
48
+ self._language = lang
49
+
50
+ def add(self, obj):
51
+ """Add a Team or Member to the cookie."""
52
+ role = Role(classname=type(obj).__name__, id=obj.id, token=obj.token)
53
+ self._roles[(role.classname, role.id)] = role
54
+
55
+ def set_language(self, lang: str):
56
+ self._language = lang
57
+
58
+ def get_language(self) -> str | None:
59
+ return self._language
60
+
61
+ @property
62
+ def cookietext(self) -> str:
63
+ data: dict = {'roles': [asdict(r) for r in self._roles.values()]}
64
+ if self._language is not None:
65
+ data['language'] = self._language
66
+ return json.dumps(data)
67
+
68
+ @property
69
+ def teams(self):
70
+ """List of Team objects stored in the cookie.
71
+
72
+ Each Team has two extra properties added:
73
+ url -- relative URL of the team's show_team page
74
+ fullurl -- absolute URL (scheme + host + path) of the same page
75
+ Requires the show_team URL to be registered; raises NoReverseMatch until then.
76
+ """
77
+ result = []
78
+ for role in self._roles.values():
79
+ if role.classname != 'Team':
80
+ continue
81
+ try:
82
+ team = Team.objects.get(id=role.id, token=role.token)
83
+ except Team.DoesNotExist:
84
+ continue
85
+ team.url = reverse('show_team', args=[team.id, team.token])
86
+ team.fullurl = self._request.build_absolute_uri(team.url)
87
+ result.append(team)
88
+ return result
89
+
90
+ @property
91
+ def members(self):
92
+ """List of Member objects stored in the cookie.
93
+
94
+ Each Member has two extra properties added:
95
+ url -- relative URL of the member's edit_member page
96
+ fullurl -- absolute URL (scheme + host + path) of the same page
97
+ Requires the edit_member URL to be registered; raises NoReverseMatch until then.
98
+ """
99
+ result = []
100
+ for role in self._roles.values():
101
+ if role.classname != 'Member':
102
+ continue
103
+ try:
104
+ member = Member.objects.get(id=role.id, token=role.token)
105
+ except Member.DoesNotExist:
106
+ continue
107
+ member.url = reverse('edit_member', args=[member.id, member.token])
108
+ member.fullurl = self._request.build_absolute_uri(member.url)
109
+ result.append(member)
110
+ return result
@@ -0,0 +1,11 @@
1
+ ---
2
+ title: This is the title of section1 (under the "Dummy" heading)
3
+ trigger: one-high(PO)
4
+ ---
5
+ # 1. Heading
6
+
7
+ Some text here.
8
+
9
+ ## 1.1 Heading
10
+
11
+ More text here, with _italics_ and **stuff**.
@@ -0,0 +1,3 @@
1
+ # README.md for `cuhasc/handbook/`
2
+
3
+ This directory contains the actual content of the handbook after which CuHaSc is named.
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 ينبغي على الأشخاص في المناصب العليا أن يتّخذوا معظم القرارات دون استشارة الأشخاص في المناصب الدنيا.
3
+ PO2 disagree5 ينبغي على الأشخاص في المناصب العليا ألّا يُكثِروا من سؤال الأشخاص في المناصب الدنيا عن آرائهم.
4
+ PO3 disagree5 ينبغي على الأشخاص في المناصب العليا أن يتجنّبوا التفاعل الاجتماعي مع الأشخاص في المناصب الدنيا.
5
+ PO4 disagree5 ينبغي على الأشخاص في المناصب الدنيا ألّا يعترضوا على القرارات التي يتّخذها الأشخاص في المناصب العليا.
6
+ PO5 disagree5 ينبغي على الأشخاص في المناصب العليا ألّا يفوّضوا المهامّ الهامّة إلى الأشخاص في المناصب الدنيا.
7
+ UN1 disagree5 من المهمّ أن تكون التعليمات موضّحةً بالتفصيل حتى أعرف دائماً ما هو متوقَّع منّي.
8
+ UN2 disagree5 من المهمّ اتّباع التعليمات والإجراءات بدقّة.
9
+ UN3 disagree5 القواعد واللوائح مهمّة لأنّها تُطلِعني على ما هو متوقَّع منّي.
10
+ UN4 disagree5 إجراءات العمل الموحّدة مفيدة.
11
+ UN5 disagree5 التعليمات المتعلّقة بتنفيذ العمليات مهمّة.
12
+ CO1 disagree5 ينبغي على الأفراد أن يضحّوا بمصلحتهم الشخصية من أجل المجموعة.
13
+ CO2 disagree5 ينبغي على الأفراد أن يتمسّكوا بالمجموعة حتى في أوقات الصعوبات.
14
+ CO3 disagree5 رفاه المجموعة أهمّ من المكافآت الفردية.
15
+ CO4 disagree5 نجاح المجموعة أهمّ من النجاح الفردي.
16
+ CO5 disagree5 ينبغي على الأفراد ألّا يسعوا إلى تحقيق أهدافهم إلّا بعد مراعاة رفاه المجموعة.
17
+ CO6 disagree5 ينبغي تشجيع الولاء للمجموعة حتى وإن تضرّرت الأهداف الفردية.
18
+ LT1 important5 الإدارة الحصيفة للمال (التوفير)
19
+ LT2 important5 المضيّ بعزمٍ رغم المعارضة (المثابرة)
20
+ LT3 important5 الثبات والاستقرار الشخصيّ
21
+ LT4 important5 التخطيط طويل الأمد
22
+ LT5 important5 التخلّي عن متعة اليوم من أجل النجاح في المستقبل
23
+ LT6 important5 العمل الجادّ من أجل النجاح في المستقبل
24
+ MA1 disagree5 أن تكون للرجال مسيرة مهنية احترافية أهمُّ من أن تكون للنساء.
25
+ MA2 disagree5 عادةً ما يحلّ الرجال المشكلات عن طريق التحليل المنطقيّ؛ بينما تحلّ النساء المشكلات عادةً عن طريق الحدس.
26
+ MA3 disagree5 عادةً ما يتطلّب حلّ المشكلات الصعبة أسلوباً نشطاً وقوياً، وهو أسلوبٌ يتّسم به الرجال عادةً.
27
+ MA4 disagree5 هناك بعض الأعمال التي يستطيع الرجل دائماً أن يؤدّيها على نحوٍ أفضل من المرأة.
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 Хората на по-високи длъжности трябва да вземат повечето решения, без да се допитват до хората на по-ниски длъжности.
3
+ PO2 disagree5 Хората на по-високи длъжности не трябва да питат твърде често за мнението на хората на по-ниски длъжности.
4
+ PO3 disagree5 Хората на по-високи длъжности трябва да избягват неформалното общуване с хората на по-ниски длъжности.
5
+ PO4 disagree5 Хората на по-ниски длъжности не трябва да изразяват несъгласие с решенията на хората на по-високи длъжности.
6
+ PO5 disagree5 Хората на по-високи длъжности не трябва да възлагат важни задачи на хората на по-ниски длъжности.
7
+ UN1 disagree5 Важно е инструкциите да са подробно описани, за да знам винаги какво се очаква от мен.
8
+ UN2 disagree5 Важно е стриктно да се спазват инструкциите и процедурите.
9
+ UN3 disagree5 Правилата и разпоредбите са важни, защото ми казват какво се очаква от мен.
10
+ UN4 disagree5 Стандартизираните работни процедури са полезни.
11
+ UN5 disagree5 Инструкциите за извършване на работните дейности са важни.
12
+ CO1 disagree5 Индивидите трябва да жертват личния си интерес заради групата.
13
+ CO2 disagree5 Индивидите трябва да остават с групата дори при трудности.
14
+ CO3 disagree5 Благополучието на групата е по-важно от индивидуалните награди.
15
+ CO4 disagree5 Успехът на групата е по-важен от индивидуалния успех.
16
+ CO5 disagree5 Индивидите трябва да преследват целите си само след като вземат предвид благополучието на групата.
17
+ CO6 disagree5 Лоялността към групата трябва да се насърчава, дори ако индивидуалните цели пострадат.
18
+ LT1 important5 Внимателно управление на парите (пестеливост)
19
+ LT2 important5 Решително продължаване на започнатото въпреки съпротивата (упоритост)
20
+ LT3 important5 Лична устойчивост и стабилност
21
+ LT4 important5 Дългосрочно планиране
22
+ LT5 important5 Отказ от днешните удоволствия заради успеха в бъдеще
23
+ LT6 important5 Усърдна работа заради успеха в бъдеще
24
+ MA1 disagree5 За мъжете е по-важно да имат професионална кариера, отколкото за жените.
25
+ MA2 disagree5 Мъжете обикновено решават проблемите чрез логически анализ; жените обикновено решават проблемите чрез интуиция.
26
+ MA3 disagree5 Решаването на трудни проблеми обикновено изисква активен, напорист подход, който е типичен за мъжете.
27
+ MA4 disagree5 Има някои видове работа, които мъжът винаги ще свърши по-добре от жената.
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 উচ্চপদস্থ ব্যক্তিদের অধিকাংশ সিদ্ধান্ত নিম্নপদস্থ ব্যক্তিদের সঙ্গে পরামর্শ না করেই নেওয়া উচিত।
3
+ PO2 disagree5 উচ্চপদস্থ ব্যক্তিদের নিম্নপদস্থ ব্যক্তিদের মতামত খুব বেশি বার জিজ্ঞাসা করা উচিত নয়।
4
+ PO3 disagree5 উচ্চপদস্থ ব্যক্তিদের নিম্নপদস্থ ব্যক্তিদের সঙ্গে সামাজিক মেলামেশা এড়িয়ে চলা উচিত।
5
+ PO4 disagree5 নিম্নপদস্থ ব্যক্তিদের উচ্চপদস্থ ব্যক্তিদের সিদ্ধান্তের সঙ্গে দ্বিমত পোষণ করা উচিত নয়।
6
+ PO5 disagree5 উচ্চপদস্থ ব্যক্তিদের গুরুত্বপূর্ণ কাজ নিম্নপদস্থ ব্যক্তিদের উপর অর্পণ করা উচিত নয়।
7
+ UN1 disagree5 নির্দেশনা বিশদভাবে দেওয়া থাকা গুরুত্বপূর্ণ, যাতে আমার কাছ থেকে কী প্রত্যাশিত তা আমি সবসময় জানতে পারি।
8
+ UN2 disagree5 নির্দেশনা ও পদ্ধতি নিবিড়ভাবে অনুসরণ করা গুরুত্বপূর্ণ।
9
+ UN3 disagree5 নিয়মকানুন গুরুত্বপূর্ণ, কারণ সেগুলি আমাকে জানায় আমার কাছ থেকে কী প্রত্যাশিত।
10
+ UN4 disagree5 মানসম্মত কর্মপদ্ধতি সহায়ক।
11
+ UN5 disagree5 কার্যপরিচালনার জন্য নির্দেশনা গুরুত্বপূর্ণ।
12
+ CO1 disagree5 ব্যক্তির উচিত গোষ্ঠীর জন্য নিজের স্বার্থ ত্যাগ করা।
13
+ CO2 disagree5 ব্যক্তির উচিত কঠিন সময়েও গোষ্ঠীর সঙ্গে থেকে যাওয়া।
14
+ CO3 disagree5 ব্যক্তিগত পুরস্কারের চেয়ে গোষ্ঠীর কল্যাণ বেশি গুরুত্বপূর্ণ।
15
+ CO4 disagree5 ব্যক্তিগত সাফল্যের চেয়ে গোষ্ঠীর সাফল্য বেশি গুরুত্বপূর্ণ।
16
+ CO5 disagree5 ব্যক্তির উচিত গোষ্ঠীর কল্যাণ বিবেচনা করার পরেই নিজের লক্ষ্য অর্জনের চেষ্টা করা।
17
+ CO6 disagree5 ব্যক্তিগত লক্ষ্য ক্ষতিগ্রস্ত হলেও গোষ্ঠীর প্রতি আনুগত্যকে উৎসাহিত করা উচিত।
18
+ LT1 important5 অর্থের সতর্ক পরিচালনা (মিতব্যয়িতা)
19
+ LT2 important5 বিরোধিতা সত্ত্বেও দৃঢ়ভাবে এগিয়ে চলা (অধ্যবসায়)
20
+ LT3 important5 ব্যক্তিগত স্থিরতা ও স্থায়িত্ব
21
+ LT4 important5 দীর্ঘমেয়াদি পরিকল্পনা
22
+ LT5 important5 ভবিষ্যতের সাফল্যের জন্য আজকের আনন্দ ছেড়ে দেওয়া
23
+ LT6 important5 ভবিষ্যতের সাফল্যের জন্য কঠোর পরিশ্রম করা
24
+ MA1 disagree5 পুরুষদের জন্য পেশাগত কর্মজীবন থাকা নারীদের তুলনায় বেশি গুরুত্বপূর্ণ।
25
+ MA2 disagree5 পুরুষেরা সাধারণত যুক্তিসংগত বিশ্লেষণের মাধ্যমে সমস্যা সমাধান করে; নারীরা সাধারণত স্বজ্ঞার মাধ্যমে সমস্যা সমাধান করে।
26
+ MA3 disagree5 কঠিন সমস্যা সমাধানে সাধারণত একটি সক্রিয় ও জোরালো পন্থা প্রয়োজন, যা পুরুষদের বৈশিষ্ট্য।
27
+ MA4 disagree5 এমন কিছু কাজ আছে যা একজন পুরুষ সবসময় একজন নারীর চেয়ে ভালোভাবে করতে পারে।
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 Lidé na vyšších pozicích by měli většinu rozhodnutí činit bez konzultace s lidmi na nižších pozicích.
3
+ PO2 disagree5 Lidé na vyšších pozicích by se neměli příliš často ptát na názory lidí na nižších pozicích.
4
+ PO3 disagree5 Lidé na vyšších pozicích by se měli vyhýbat společenským kontaktům s lidmi na nižších pozicích.
5
+ PO4 disagree5 Lidé na nižších pozicích by neměli nesouhlasit s rozhodnutími lidí na vyšších pozicích.
6
+ PO5 disagree5 Lidé na vyšších pozicích by neměli delegovat důležité úkoly na lidi na nižších pozicích.
7
+ UN1 disagree5 Je důležité mít podrobně rozepsané pokyny, díky čemuž vždy vím, co se ode mě očekává.
8
+ UN2 disagree5 Je důležité přesně dodržovat pokyny a postupy.
9
+ UN3 disagree5 Pravidla a předpisy jsou důležité, protože mě informují o tom, co se ode mě očekává.
10
+ UN4 disagree5 Standardizované pracovní postupy jsou užitečné.
11
+ UN5 disagree5 Pokyny pro pracovní činnosti jsou důležité.
12
+ CO1 disagree5 Jednotlivci by měli obětovat vlastní zájmy ve prospěch skupiny.
13
+ CO2 disagree5 Jednotlivci by se měli držet skupiny i v těžkých časech.
14
+ CO3 disagree5 Dobro skupiny je důležitější než individuální odměny.
15
+ CO4 disagree5 Úspěch skupiny je důležitější než individuální úspěch.
16
+ CO5 disagree5 Jednotlivci by měli usilovat o své cíle až poté, co vezmou v úvahu dobro skupiny.
17
+ CO6 disagree5 Loajalita ke skupině by měla být podporována, i když tím trpí individuální cíle.
18
+ LT1 important5 Pečlivé hospodaření s penězi (šetrnost)
19
+ LT2 important5 Odhodlané pokračování navzdory odporu (vytrvalost)
20
+ LT3 important5 Osobní stálost a stabilita
21
+ LT4 important5 Dlouhodobé plánování
22
+ LT5 important5 Vzdání se dnešní zábavy pro budoucí úspěch
23
+ LT6 important5 Tvrdá práce pro budoucí úspěch
24
+ MA1 disagree5 Mít profesní kariéru je důležitější pro muže než pro ženy.
25
+ MA2 disagree5 Muži obvykle řeší problémy pomocí logické analýzy; ženy obvykle řeší problémy pomocí intuice.
26
+ MA3 disagree5 Řešení obtížných problémů obvykle vyžaduje aktivní, razantní přístup, který je typický pro muže.
27
+ MA4 disagree5 Existují práce, které muž vždy zvládne lépe než žena.
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 Personer i højere positioner bør træffe de fleste beslutninger uden at rådføre sig med personer i lavere positioner.
3
+ PO2 disagree5 Personer i højere positioner bør ikke for ofte spørge personer i lavere positioner om deres mening.
4
+ PO3 disagree5 Personer i højere positioner bør undgå social omgang med personer i lavere positioner.
5
+ PO4 disagree5 Personer i lavere positioner bør ikke være uenige i beslutninger truffet af personer i højere positioner.
6
+ PO5 disagree5 Personer i højere positioner bør ikke uddelegere vigtige opgaver til personer i lavere positioner.
7
+ UN1 disagree5 Det er vigtigt, at instruktioner er beskrevet i detaljer, så jeg altid ved, hvad der forventes af mig.
8
+ UN2 disagree5 Det er vigtigt at følge instruktioner og procedurer nøje.
9
+ UN3 disagree5 Regler og forskrifter er vigtige, fordi de informerer mig om, hvad der forventes af mig.
10
+ UN4 disagree5 Standardiserede arbejdsprocedurer er nyttige.
11
+ UN5 disagree5 Instruktioner til arbejdsopgaver er vigtige.
12
+ CO1 disagree5 Individer bør ofre deres egeninteresse for gruppens skyld.
13
+ CO2 disagree5 Individer bør blive i gruppen, selv når der er vanskeligheder.
14
+ CO3 disagree5 Gruppens vel er vigtigere end individuelle belønninger.
15
+ CO4 disagree5 Gruppens succes er vigtigere end individuel succes.
16
+ CO5 disagree5 Individer bør først forfølge deres mål, når de har taget hensyn til gruppens vel.
17
+ CO6 disagree5 Gruppeloyalitet bør opmuntres, selv hvis individuelle mål lider under det.
18
+ LT1 important5 Omhyggelig omgang med penge (Sparsommelighed)
19
+ LT2 important5 At fortsætte beslutsomt trods modstand (Vedholdenhed)
20
+ LT3 important5 Personlig standhaftighed og stabilitet
21
+ LT4 important5 Langsigtet planlægning
22
+ LT5 important5 At give afkald på nutidens fornøjelser for succes i fremtiden
23
+ LT6 important5 At arbejde hårdt for succes i fremtiden
24
+ MA1 disagree5 Det er vigtigere for mænd at have en professionel karriere, end det er for kvinder.
25
+ MA2 disagree5 Mænd løser som regel problemer med logisk analyse; kvinder løser som regel problemer med intuition.
26
+ MA3 disagree5 At løse vanskelige problemer kræver som regel en aktiv, kraftfuld tilgang, som er typisk for mænd.
27
+ MA4 disagree5 Der er nogle opgaver, som en mand altid kan udføre bedre end en kvinde.
@@ -0,0 +1,27 @@
1
+ Item Scale Content
2
+ PO1 disagree5 Personen in höheren Positionen sollten die meisten Entscheidungen treffen, ohne Personen in niedrigeren Positionen zu konsultieren.
3
+ PO2 disagree5 Personen in höheren Positionen sollten nicht zu häufig nach den Meinungen von Personen in niedrigeren Positionen fragen.
4
+ PO3 disagree5 Personen in höheren Positionen sollten sozialen Umgang mit Personen in niedrigeren Positionen vermeiden.
5
+ PO4 disagree5 Personen in niedrigeren Positionen sollten Entscheidungen von Personen in höheren Positionen nicht widersprechen.
6
+ PO5 disagree5 Personen in höheren Positionen sollten wichtige Aufgaben nicht an Personen in niedrigeren Positionen delegieren.
7
+ UN1 disagree5 Es ist wichtig, detaillierte Anweisungen zu haben, damit ich immer weiß, was von mir erwartet wird.
8
+ UN2 disagree5 Es ist wichtig, Anweisungen und Verfahrensweisen genau zu befolgen.
9
+ UN3 disagree5 Regeln und Vorschriften sind wichtig, weil sie mir sagen, was von mir erwartet wird.
10
+ UN4 disagree5 Standardisierte Arbeitsabläufe sind hilfreich.
11
+ UN5 disagree5 Arbeitsanweisungen sind wichtig.
12
+ CO1 disagree5 Einzelpersonen sollten ihr Eigeninteresse zugunsten der Gruppe opfern.
13
+ CO2 disagree5 Einzelpersonen sollten auch in schwierigen Zeiten der Gruppe treu bleiben.
14
+ CO3 disagree5 Das Wohlergehen der Gruppe ist wichtiger als individuelle Belohnungen.
15
+ CO4 disagree5 Gruppenerfolg ist wichtiger als individueller Erfolg.
16
+ CO5 disagree5 Einzelpersonen sollten ihre Ziele erst verfolgen, nachdem sie das Wohlergehen der Gruppe berücksichtigt haben.
17
+ CO6 disagree5 Gruppenloyalität sollte gefördert werden, auch wenn individuelle Ziele darunter leiden.
18
+ LT1 important5 Sorgfältiger Umgang mit Geld (Sparsamkeit)
19
+ LT2 important5 Entschlossenes Weitermachen trotz Widerstand (Beharrlichkeit)
20
+ LT3 important5 Persönliche Beständigkeit und Stabilität
21
+ LT4 important5 Langfristige Planung
22
+ LT5 important5 Auf heutiges Vergnügen zugunsten künftigen Erfolgs verzichten
23
+ LT6 important5 Harte Arbeit für zukünftigen Erfolg
24
+ MA1 disagree5 Für Männer ist es wichtiger, eine berufliche Karriere zu haben, als für Frauen.
25
+ MA2 disagree5 Männer lösen Probleme meist durch logische Analyse; Frauen lösen Probleme meist durch Intuition.
26
+ MA3 disagree5 Das Lösen schwieriger Probleme erfordert üblicherweise ein aktives, energisches Vorgehen, wie es typisch für Männer ist.
27
+ MA4 disagree5 Es gibt Tätigkeiten, die ein Mann immer besser ausführen kann als eine Frau.