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.
- cuhasc/__init__.py +0 -0
- cuhasc/admin.py +3 -0
- cuhasc/apps.py +5 -0
- cuhasc/base.py +18 -0
- cuhasc/cli.py +277 -0
- cuhasc/constants.py +11 -0
- cuhasc/cookies.py +110 -0
- cuhasc/data/handbook/Dummy-section1.md +11 -0
- cuhasc/data/handbook/README.md +3 -0
- cuhasc/data/instruments/cvscale-ar.tsv +27 -0
- cuhasc/data/instruments/cvscale-bg.tsv +27 -0
- cuhasc/data/instruments/cvscale-bn.tsv +27 -0
- cuhasc/data/instruments/cvscale-cs.tsv +27 -0
- cuhasc/data/instruments/cvscale-da.tsv +27 -0
- cuhasc/data/instruments/cvscale-de.tsv +27 -0
- cuhasc/data/instruments/cvscale-el.tsv +27 -0
- cuhasc/data/instruments/cvscale-en.tsv +27 -0
- cuhasc/data/instruments/cvscale-es.tsv +27 -0
- cuhasc/data/instruments/cvscale-et.tsv +27 -0
- cuhasc/data/instruments/cvscale-fa.tsv +27 -0
- cuhasc/data/instruments/cvscale-fi.tsv +27 -0
- cuhasc/data/instruments/cvscale-fr.tsv +27 -0
- cuhasc/data/instruments/cvscale-hi.tsv +27 -0
- cuhasc/data/instruments/cvscale-hr.tsv +27 -0
- cuhasc/data/instruments/cvscale-hu.tsv +27 -0
- cuhasc/data/instruments/cvscale-id.tsv +27 -0
- cuhasc/data/instruments/cvscale-it.tsv +27 -0
- cuhasc/data/instruments/cvscale-ja.tsv +27 -0
- cuhasc/data/instruments/cvscale-ko.tsv +27 -0
- cuhasc/data/instruments/cvscale-lt.tsv +27 -0
- cuhasc/data/instruments/cvscale-lv.tsv +27 -0
- cuhasc/data/instruments/cvscale-nl.tsv +27 -0
- cuhasc/data/instruments/cvscale-no.tsv +27 -0
- cuhasc/data/instruments/cvscale-pl.tsv +27 -0
- cuhasc/data/instruments/cvscale-pt.tsv +27 -0
- cuhasc/data/instruments/cvscale-ro.tsv +27 -0
- cuhasc/data/instruments/cvscale-ru.tsv +27 -0
- cuhasc/data/instruments/cvscale-sk.tsv +27 -0
- cuhasc/data/instruments/cvscale-sl.tsv +27 -0
- cuhasc/data/instruments/cvscale-sq.tsv +27 -0
- cuhasc/data/instruments/cvscale-sr.tsv +27 -0
- cuhasc/data/instruments/cvscale-sv.tsv +27 -0
- cuhasc/data/instruments/cvscale-ta.tsv +27 -0
- cuhasc/data/instruments/cvscale-th.tsv +27 -0
- cuhasc/data/instruments/cvscale-tr.tsv +27 -0
- cuhasc/data/instruments/cvscale-uk.tsv +27 -0
- cuhasc/data/instruments/cvscale-ur.tsv +27 -0
- cuhasc/data/instruments/cvscale-vi.tsv +27 -0
- cuhasc/data/instruments/cvscale-zh.tsv +27 -0
- cuhasc/data/instruments/dimensions-ar.csv +6 -0
- cuhasc/data/instruments/dimensions-bg.csv +6 -0
- cuhasc/data/instruments/dimensions-bn.csv +6 -0
- cuhasc/data/instruments/dimensions-cs.csv +6 -0
- cuhasc/data/instruments/dimensions-da.csv +6 -0
- cuhasc/data/instruments/dimensions-de.csv +6 -0
- cuhasc/data/instruments/dimensions-el.csv +6 -0
- cuhasc/data/instruments/dimensions-en.csv +6 -0
- cuhasc/data/instruments/dimensions-es.csv +6 -0
- cuhasc/data/instruments/dimensions-et.csv +6 -0
- cuhasc/data/instruments/dimensions-fa.csv +6 -0
- cuhasc/data/instruments/dimensions-fi.csv +6 -0
- cuhasc/data/instruments/dimensions-fr.csv +6 -0
- cuhasc/data/instruments/dimensions-hi.csv +6 -0
- cuhasc/data/instruments/dimensions-hr.csv +6 -0
- cuhasc/data/instruments/dimensions-hu.csv +6 -0
- cuhasc/data/instruments/dimensions-id.csv +6 -0
- cuhasc/data/instruments/dimensions-it.csv +6 -0
- cuhasc/data/instruments/dimensions-ja.csv +6 -0
- cuhasc/data/instruments/dimensions-ko.csv +6 -0
- cuhasc/data/instruments/dimensions-lt.csv +6 -0
- cuhasc/data/instruments/dimensions-lv.csv +6 -0
- cuhasc/data/instruments/dimensions-nl.csv +6 -0
- cuhasc/data/instruments/dimensions-no.csv +6 -0
- cuhasc/data/instruments/dimensions-pl.csv +6 -0
- cuhasc/data/instruments/dimensions-pt.csv +6 -0
- cuhasc/data/instruments/dimensions-ro.csv +6 -0
- cuhasc/data/instruments/dimensions-ru.csv +6 -0
- cuhasc/data/instruments/dimensions-sk.csv +6 -0
- cuhasc/data/instruments/dimensions-sl.csv +6 -0
- cuhasc/data/instruments/dimensions-sq.csv +6 -0
- cuhasc/data/instruments/dimensions-sr.csv +6 -0
- cuhasc/data/instruments/dimensions-sv.csv +6 -0
- cuhasc/data/instruments/dimensions-ta.csv +6 -0
- cuhasc/data/instruments/dimensions-th.csv +6 -0
- cuhasc/data/instruments/dimensions-tr.csv +6 -0
- cuhasc/data/instruments/dimensions-uk.csv +6 -0
- cuhasc/data/instruments/dimensions-ur.csv +6 -0
- cuhasc/data/instruments/dimensions-vi.csv +6 -0
- cuhasc/data/instruments/dimensions-zh.csv +6 -0
- cuhasc/data/instruments/scales-ar.csv +3 -0
- cuhasc/data/instruments/scales-bg.csv +3 -0
- cuhasc/data/instruments/scales-bn.csv +3 -0
- cuhasc/data/instruments/scales-cs.csv +3 -0
- cuhasc/data/instruments/scales-da.csv +3 -0
- cuhasc/data/instruments/scales-de.csv +3 -0
- cuhasc/data/instruments/scales-el.csv +3 -0
- cuhasc/data/instruments/scales-en.csv +3 -0
- cuhasc/data/instruments/scales-es.csv +3 -0
- cuhasc/data/instruments/scales-et.csv +3 -0
- cuhasc/data/instruments/scales-fa.csv +3 -0
- cuhasc/data/instruments/scales-fi.csv +3 -0
- cuhasc/data/instruments/scales-fr.csv +3 -0
- cuhasc/data/instruments/scales-hi.csv +3 -0
- cuhasc/data/instruments/scales-hr.csv +3 -0
- cuhasc/data/instruments/scales-hu.csv +3 -0
- cuhasc/data/instruments/scales-id.csv +3 -0
- cuhasc/data/instruments/scales-it.csv +3 -0
- cuhasc/data/instruments/scales-ja.csv +3 -0
- cuhasc/data/instruments/scales-ko.csv +3 -0
- cuhasc/data/instruments/scales-lt.csv +3 -0
- cuhasc/data/instruments/scales-lv.csv +3 -0
- cuhasc/data/instruments/scales-nl.csv +3 -0
- cuhasc/data/instruments/scales-no.csv +3 -0
- cuhasc/data/instruments/scales-pl.csv +3 -0
- cuhasc/data/instruments/scales-pt.csv +3 -0
- cuhasc/data/instruments/scales-ro.csv +3 -0
- cuhasc/data/instruments/scales-ru.csv +3 -0
- cuhasc/data/instruments/scales-sk.csv +3 -0
- cuhasc/data/instruments/scales-sl.csv +3 -0
- cuhasc/data/instruments/scales-sq.csv +3 -0
- cuhasc/data/instruments/scales-sr.csv +3 -0
- cuhasc/data/instruments/scales-sv.csv +3 -0
- cuhasc/data/instruments/scales-ta.csv +3 -0
- cuhasc/data/instruments/scales-th.csv +3 -0
- cuhasc/data/instruments/scales-tr.csv +3 -0
- cuhasc/data/instruments/scales-uk.csv +3 -0
- cuhasc/data/instruments/scales-ur.csv +3 -0
- cuhasc/data/instruments/scales-vi.csv +3 -0
- cuhasc/data/instruments/scales-zh.csv +3 -0
- cuhasc/deployment.py +105 -0
- cuhasc/forms.py +39 -0
- cuhasc/handbook.py +193 -0
- cuhasc/i18n.py +1153 -0
- cuhasc/instruments.py +111 -0
- cuhasc/management/__init__.py +0 -0
- cuhasc/management/commands/__init__.py +0 -0
- cuhasc/management/commands/adminpage.py +31 -0
- cuhasc/migrations/0001_initial.py +22 -0
- cuhasc/migrations/0002_team_member_token.py +19 -0
- cuhasc/migrations/0003_member.py +23 -0
- cuhasc/migrations/0004_qresult.py +26 -0
- cuhasc/migrations/0005_adminpage.py +20 -0
- cuhasc/migrations/0006_member_order_seed.py +19 -0
- cuhasc/migrations/__init__.py +0 -0
- cuhasc/models.py +104 -0
- cuhasc/plots.py +103 -0
- cuhasc/settings.py +113 -0
- cuhasc/static/cuhasc/cuhasc-logo.svg +9 -0
- cuhasc/static/cuhasc/cuhasc.css +104 -0
- cuhasc/static/cuhasc/favicon.png +0 -0
- cuhasc/static/cuhasc/vendor/bootstrap/bootstrap.bundle.min.js +7 -0
- cuhasc/static/cuhasc/vendor/bootstrap/bootstrap.min.css +6 -0
- cuhasc/templates/cuhasc/_language_selector.html +23 -0
- cuhasc/templates/cuhasc/adminpage.html +24 -0
- cuhasc/templates/cuhasc/base.html +28 -0
- cuhasc/templates/cuhasc/create_member.html +35 -0
- cuhasc/templates/cuhasc/create_team.html +12 -0
- cuhasc/templates/cuhasc/edit_member.html +34 -0
- cuhasc/templates/cuhasc/edit_team.html +12 -0
- cuhasc/templates/cuhasc/handbook_section.html +8 -0
- cuhasc/templates/cuhasc/home.html +45 -0
- cuhasc/templates/cuhasc/show_member.html +16 -0
- cuhasc/templates/cuhasc/show_team.html +47 -0
- cuhasc/test/__init__.py +0 -0
- cuhasc/test/test_base.py +18 -0
- cuhasc/test/test_cli.py +161 -0
- cuhasc/test/test_deployment.py +77 -0
- cuhasc/test/test_handbook.py +240 -0
- cuhasc/test/test_handbook_views.py +61 -0
- cuhasc/test/test_i18n.py +21 -0
- cuhasc/test/test_instruments.py +57 -0
- cuhasc/test/test_models.py +128 -0
- cuhasc/test/test_settings.py +18 -0
- cuhasc/test/test_views.py +295 -0
- cuhasc/urls.py +15 -0
- cuhasc/views.py +304 -0
- cuhasc/wsgi.py +16 -0
- cuhasc-0.6.0.dist-info/METADATA +241 -0
- cuhasc-0.6.0.dist-info/RECORD +182 -0
- cuhasc-0.6.0.dist-info/WHEEL +4 -0
- cuhasc-0.6.0.dist-info/entry_points.txt +2 -0
- cuhasc-0.6.0.dist-info/licenses/LICENSE +21 -0
cuhasc/__init__.py
ADDED
|
File without changes
|
cuhasc/admin.py
ADDED
cuhasc/apps.py
ADDED
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,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.
|