Skip to content
Commits on Source (1938)
#! /bin/bash
mypy relate course accounts prairietest
#! /bin/bash
# 38678: django-celery-results: no update currently available
# https://github.com/celery/django-celery-results/issues/154
# 39253: py.path.svnwc DOS
# 39535:
# https://github.com/inducer/relate/pull/779
# amounts to pysaml2 >= 6.5.1 version bump, done in pyproject.toml
# 40291: affects pip, not related to relate's safety
# code not used
# 4471{5,6,7}: We're not using numpy in a user-exposed-manner.
# 51549: No call path from relate to mpmathify
# 51499: Not calling wheel in a safety-related manner
# 51457: not calling py in a safety-related manner
# 65213: nonsense according to
# https://github.com/pyca/pyopenssl/issues/1300
# 67599: pip issue, utter nonsense
# 70612: Jinja2 SSTI, as of https://github.com/inducer/relate/pull/1053
# there is no longer a direct Jinja dependency, and no known path to SSTI.
safety check \
-i 38678 \
-i 39253 \
-i 39535 \
-i 40291 \
-i 44715 \
-i 44716 \
-i 44717 \
-i 51159 \
-i 51549 \
-i 51499 \
-i 51457 \
-i 65213 \
-i 67599 \
-i 70612 \
--full-report
#! /bin/bash
set -e
# collectstatic and i18n a local_settings file
cp local_settings_example.py local_settings.py
echo "OSTYPE: $OSTYPE"
if [[ "$OSTYPE" != msys ]]; then
echo "i18n"
# Make sure i18n literals marked correctly
poetry run python manage.py makemessages --all
poetry run python manage.py compilemessages
fi
staticfiles=(
bundle-base.js
bundle-base-with-markup.js
bundle-codemirror.js
bundle-datatables.js
bundle-fullcalendar.js
bundle-prosemirror.js
bundle-prosemirror.js
tex-svg.js
)
mkdir -p frontend-dist
for i in "${staticfiles[@]}"; do
touch "frontend-dist/$i"
done
poetry run python manage.py collectstatic
rm local_settings.py
echo "Start testing"
export RELATE_LOCAL_TEST_SETTINGS="local_settings_example.py"
PYTEST_COMMON_FLAGS=()
if test "$CI_SERVER_NAME" = "GitLab"; then
# I don't *really* know what's going on, but I observed EADDRNOTAVAIL
# when the tests try to connect to the code grading process.
#
# Sample failed job:
# https://gitlab.tiker.net/inducer/relate/-/jobs/159522
# -AK, 2020-09-01
PYTEST_COMMON_FLAGS+=(-k "not LanguageOverrideTest")
fi
if [[ "$RL_CI_TEST" = "expensive" ]]; then
echo "Expensive tests"
poetry run python -m pytest "${PYTEST_COMMON_FLAGS[@]}" --slow
elif [[ "$RL_CI_TEST" = "postgres" ]]; then
export PGPASSWORD=relatepgpass
echo "Preparing database"
echo "import psycopg2.extensions" >> local_settings_example.py
echo "DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': 'localhost',
'USER': 'postgres',
'PASSWORD': '${PGPASSWORD}',
'NAME': 'test_relate',
'OPTIONS': {
'isolation_level': psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE,
},
},
}" >> local_settings_example.py
poetry run pip install psycopg2
echo "Database tests"
poetry run python -m pytest "${PYTEST_COMMON_FLAGS[@]}"
else
echo "Base tests"
poetry run python -m pytest "${PYTEST_COMMON_FLAGS[@]}"
fi
#! /bin/bash
set -e
set -x
PY_EXE="$1"
if test "$PY_EXE" = ""; then
PY_EXE="python3.6"
fi
shift
echo "-----------------------------------------------"
echo "Current directory: $(pwd)"
echo "Python executable: ${PY_EXE}"
echo "-----------------------------------------------"
# {{{ clean up
rm -Rf .env
rm -Rf build
find . -name '*.pyc' -delete
rm -Rf env
git clean -fdx -e siteconf.py -e boost-numeric-bindings -e local_settings.py
if test `find "siteconf.py" -mmin +1`; then
echo "siteconf.py older than a minute, assumed stale, deleted"
rm -f siteconf.py
fi
# }}}
# {{{ virtualenv
${PY_EXE} -m ensurepip
${PY_EXE} -m pip install poetry
# }}}
poetry install
poetry shell
git clone https://github.com/inducer/relate-sample
cd relate-sample
poetry run relate validate .
poetry run relate test-code questions/autograded-python-example.yml
poetry run relate expand-yaml flows/quiz-test.yml > /dev/null
# https://editorconfig.org/
# https://github.com/editorconfig/editorconfig-vim
# https://github.com/editorconfig/editorconfig-emacs
root = true
[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
indent_size = 4
[*.rst]
indent_size = 4
[*.cpp]
indent_size = 2
[*.hpp]
indent_size = 2
# There may be one in doc/
[Makefile]
indent_style = tab
# https://github.com/microsoft/vscode/issues/1679
[*.md]
trim_trailing_whitespace = false
module.exports = {
env: {
browser: true,
es2021: true,
},
extends: [
'airbnb-base',
],
parserOptions: {
ecmaVersion: 13,
sourceType: 'module',
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: '^_' }]
},
};
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "monthly"
# vim: sw=4
name: Gitlab mirror
on:
push:
branches:
- main
jobs:
autopush:
name: Automatic push to gitlab.tiker.net
if: startsWith(github.repository, 'inducer/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
curl -L -O https://tiker.net/ci-support-v0
. ./ci-support-v0
mirror_github_to_gitlab
env:
GITLAB_AUTOPUSH_KEY: ${{ secrets.GITLAB_AUTOPUSH_KEY }}
# vim: sw=4
name: CI
on:
push:
branches:
- main
pull_request:
schedule:
- cron: "17 3 * * 0"
defaults:
run:
# required by https://github.com/snok/install-poetry#windows
shell: bash
concurrency:
group: ${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
jobs:
lint:
name: Lint and typecheck Python
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install Poetry
uses: snok/install-poetry@v1.4.1
with:
version: "1.8.2"
virtualenvs-create: true
#- name: Cache python dependencies
# uses: actions/cache@v2
# id: cache
# with:
# path: ~/.cache/pypoetry
# key: poetry-${{ hashFiles('**/poetry.lock') }}-${{ matrix.python-version }}
# restore-keys: |
# poetry-${{ hashFiles('**/poetry.lock') }}-${{ matrix.python-version }}
- name: Install Dependencies
run: poetry install
# if: steps.cache.outputs.cache-hit != 'true'
- name: "Ruff"
run: |
poetry run ruff check
- uses: crate-ci/typos@master
- name: "Set up local settings"
run: cp local_settings_example.py local_settings.py
- name: "Mypy"
run: poetry run ./.ci/run-mypy.sh
- name: "Safety"
run: poetry run ./.ci/run-safety.sh
- name: "Sphinx"
run: |
(cd doc; poetry run make html SPHINXOPTS="-W --keep-going -n")
frontend:
name: Lint JS/build frontend (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['16', '18', '20']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install
run: npm install
- name: ESLint
run: npx eslint frontend/js/*.js
- name: Rollup build
run: npm run build
pytest:
name: Python ${{ matrix.python-version }} - ${{ matrix.suite }}
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.10', '3.11', '3.x']
suite: ['base', 'postgres', 'expensive']
services:
postgres:
image: postgres
env:
POSTGRES_PASSWORD: relatepgpass
POSTGRES_DB: test_relate
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Poetry
uses: snok/install-poetry@v1.4.1
with:
version: "1.8.2"
virtualenvs-create: true
#- name: Cache python dependencies
# uses: actions/cache@v2
# id: cache
# with:
# path: ~/.cache/pypoetry
# key: poetry-${{ hashFiles('**/poetry.lock') }}-${{ matrix.python-version }}
# restore-keys: |
# poetry-${{ hashFiles('**/poetry.lock') }}-${{ matrix.python-version }}
- name: Install Dependencies
run: poetry install
# if: steps.cache.outputs.cache-hit != 'true'
- name: Install OS dependencies
env:
DEBIAN_FRONTEND: noninteractive
run: |
sudo apt-get install gettext
- name: Run test suite
env:
RL_CI_TEST: ${{ matrix.suite }}
run: |
bash ./.ci/run-tests-for-ci.sh
- name: Test command line tool
if: matrix.suite == 'base'
run: |
git clone https://github.com/inducer/relate-sample
cd relate-sample
poetry run relate validate .
poetry run relate test-code questions/autograded-python-example.yml
poetry run relate expand-yaml flows/quiz-test.yml > /dev/null
pytest-windows:
name: Python - Windows
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install Poetry
uses: snok/install-poetry@v1.4.1
with:
version: "1.8.2"
virtualenvs-create: true
- name: Configure Poetry
run: |
poetry config virtualenvs.in-project true
- name: Install Dependencies
run: |
poetry install
- name: Run test suite
run: |
bash ./.ci/run-tests-for-ci.sh
# vim: sw=2
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ main ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ main ]
schedule:
- cron: '35 12 * * 3'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: [ 'javascript', 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
# Learn more:
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
......@@ -7,10 +7,16 @@ local_settings.py
*.py[co]
.sw[opn]
doc/_build
components
node_modules
poetry.toml
setuptools-[0-9]*
virtualenv-[0-9]*
*.egg-info
build
/dist/
*.pyz
......@@ -20,3 +26,19 @@ saml_config.py
*.pem
git-roots
.mypy_cache
/.idea
/.env
bulk-storage
/.coverage
/coverage.xml
/.vscode/
/.venv/
tags
frontend-dist
"Python 2.7":
script:
- "PY_EXE=python2.7 bash ./run-tests-for-ci.sh"
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
POETRY_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pypoetry"
POETRY_VIRTUALENVS_IN_PROJECT: "true"
cache:
key:
files:
- .gitlab-ci.yml
- poetry.lock
prefix: relate
paths:
- ".cache/pypoetry"
- ".cache/pip"
- ".venv"
stages:
- setup
- lint
- tests
- docs
.install-deps-template: &install-deps
image: inducer/ci-base-image
tags:
- python2.7
- docker-runner
before_script:
- curl -sSL https://install.python-poetry.org | python3 -
- export PATH="$HOME/.local/bin:$PATH"
- poetry install
.quality-template: &quality
<<: *install-deps
stage: lint
except:
- tags
- tags
Python 3.5:
.test-template: &test
<<: *install-deps
script:
- "PY_EXE=python3.5 bash ./run-tests-for-ci.sh"
tags:
- python3.5
- "bash ./.ci/run-tests-for-ci.sh"
stage: tests
except:
- tags
- tags
coverage: "/TOTAL.+ ([0-9]{1,3}%)/"
Documentation:
setup:
<<: *install-deps
stage: setup
script: poetry config --list
ruff:
<<: *quality
script: |
poetry run ruff check
mypy:
<<: *quality
variables:
RELATE_LOCAL_TEST_SETTINGS: './local_settings_example.py'
script: poetry run ./.ci/run-mypy.sh
safety:
<<: *quality
script: poetry run ./.ci/run-safety.sh
Python 3:
<<: *test
needs: [setup]
Python 3 Expensive:
<<: *test
variables:
RL_CI_TEST: expensive
needs: [setup]
Python 3 CLI Tool:
<<: *test
script:
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/build-docs.sh
- "cp local_settings.example.py local_settings.py"
- ". ./build-docs.sh"
tags:
- python3.5
only:
- master
- git clone https://github.com/inducer/relate-sample
- cd relate-sample
- poetry run relate validate .
- poetry run relate test-code questions/autograded-python-example.yml
- poetry run relate expand-yaml flows/quiz-test.yml > /dev/null
needs: [setup]
Mypy:
Documentation:
<<: *install-deps
stage: docs
variables:
RELATE_LOCAL_TEST_SETTINGS: './local_settings_example.py'
script:
- curl -L -O -k https://gitlab.tiker.net/inducer/ci-support/raw/master/prepare-and-run-mypy.sh
- "cp local_settings.example.py local_settings.py"
- ". ./prepare-and-run-mypy.sh"
tags:
- python3.5
except:
- tags
- poetry run bash ./doc/build-docs.sh
needs: [setup]
RELATE is licensed to you under the MIT/X Consortium license:
Copyright (c) 2014-15 Andreas Klöckner and Contributors.
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
......@@ -3,8 +3,18 @@ RELATE
Relate is an Environment for Learning And TEaching
.. image:: https://gitlab.tiker.net/inducer/relate/badges/main/pipeline.svg
:alt: Gitlab Build Status
:target: https://gitlab.tiker.net/inducer/relate/commits/main
.. image:: https://github.com/inducer/relate/workflows/CI/badge.svg?branch=main
:alt: Github Build Status
:target: https://github.com/inducer/relate/actions?query=branch%3Amain+workflow%3ACI
.. image:: https://badge.fury.io/py/relate-courseware.png
:alt: Python Package Index Release Page
:target: https://pypi.org/project/relate-courseware/
+----------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+
| .. image:: https://raw.githubusercontent.com/inducer/relate/master/doc/images/screenshot.png | .. image:: https://raw.githubusercontent.com/inducer/relate/master/doc/images/screenshot-2.png |
| .. image:: https://raw.githubusercontent.com/inducer/relate/main/doc/images/screenshot.png | .. image:: https://raw.githubusercontent.com/inducer/relate/main/doc/images/screenshot-2.png |
+----------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------+
RELATE is a web-based courseware package. It is set apart by the following
......@@ -15,7 +25,6 @@ features:
* Simple, text-based format for reusable course content
* Based on standard `YAML <https://en.wikipedia.org/wiki/YAML>`_,
`Markdown <https://en.wikipedia.org/wiki/Markdown>`_
* Instructors can implement custom question/page types in Python.
See `example content <https://github.com/inducer/relate-sample>`_.
......@@ -37,9 +46,6 @@ features:
* Facilitates live quizzes in the classroom.
* In-class instant messaging via XMPP.
Works well with `xmpp-popup <https://github.com/inducer/xmpp-popup>`_.
* Built-in support for `VideoJS <http://www.videojs.com/>`_ offers
easy-to-use support for integrating HTML5 video into course content
without the need for third-party content hosting.
Links
-----
......
# Security Policy
## Supported Versions
Only the `main` git branch can be expected to include fixes for security issues.
## Reporting a Vulnerability
Please email <inform@tiker.net> with security issues. You may use the GPG key with
fingerprint `71AA298BCA171145` to encrypt your communication.
- Narrow form rendering
- MOSS submission
- Custom matcher classes
- Enter single grades
- Show human/robot grader point parts
Ideas
......@@ -21,12 +16,8 @@ Ideas
- Page URL endpoints (see page-url-endpoints branch)
- 'Save and next' for grading
- Fixed grading panel?
- Change "end session" link for sessions that don't actually end.
- Manual grade entry/override
- Flow results analytics
......
default_app_config = 'accounts.apps.AccountsConfig'
\ No newline at end of file
# (empty)
# -*- coding: utf-8 -*-
from __future__ import annotations
from __future__ import division
__copyright__ = "Copyright (C) 2014 Andreas Kloeckner"
......@@ -27,48 +26,66 @@ THE SOFTWARE.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as UserAdminBase
from django.utils.translation import ugettext_lazy as _ # noqa
from . models import User
from django.utils.translation import gettext_lazy as _
from course.admin import _filter_course_linked_obj_for_user, _filter_courses_for_user
from course.models import Course, Participation
def _remove_from_fieldsets(fs, field_name):
return tuple(
(heading, {"fields":
tuple(
f for f in props["fields"]
if f != field_name)})
for heading, props in fs)
from .models import User
class UserAdmin(UserAdminBase):
# list_display = tuple(
# f for f in UserAdminBase.list_display
# if f != "is_staff")
# list_filter = tuple(
# f for f in UserAdminBase.list_filter
# if f != "is_staff")
# fieldsets = _remove_from_fieldsets(
# UserAdminBase.fieldsets, "is_staff")
def _get_filter_participations_for_user(user):
participations = Participation.objects.all()
if not user.is_superuser:
participations = _filter_course_linked_obj_for_user(participations, user)
return participations
class CourseListFilter(admin.SimpleListFilter):
title = _("Course")
parameter_name = "course__identifier"
def lookups(self, request, model_admin):
course_identifiers = (
_filter_courses_for_user(Course.objects, request.user)
.values_list("identifier", flat=True))
return zip(course_identifiers, course_identifiers, strict=True)
def queryset(self, request, queryset):
if self.value():
participations = (
_get_filter_participations_for_user(request.user)
.filter(course__identifier=self.value()))
return queryset.filter(pk__in=participations.values_list("user__pk"))
else:
return queryset
@admin.register(User)
class UserAdmin(UserAdminBase):
save_on_top = True
list_display = tuple(UserAdminBase.list_display) + (
"name_verified",
"status",
"institutional_id", "institutional_id_verified",
)
# Fixing this type-ignore would require type.__getitem__ on Django types,
# which is only available via monkeypatching, ugh.
list_display = (
*UserAdminBase.list_display, # type: ignore[misc]
"name_verified", "status", "institutional_id", "institutional_id_verified")
list_editable = ("first_name", "last_name",
"name_verified",
"status",
"institutional_id", "institutional_id_verified",
"name_verified",)
list_filter = tuple(UserAdminBase.list_filter) + (
"status", "participations__course")
search_fields = tuple(UserAdminBase.search_fields) + (
"institutional_id",)
list_filter = (
*UserAdminBase.list_filter,
"status", CourseListFilter) # type: ignore
search_fields = (*tuple(UserAdminBase.search_fields), "institutional_id")
_fsets = UserAdminBase.fieldsets
assert _fsets is not None
fieldsets = UserAdminBase.fieldsets[:1] + (
(UserAdminBase.fieldsets[1][0], {"fields": (
fieldsets = (
*_fsets[:1],
(_fsets[1][0], {"fields": (
"status",
"first_name",
"last_name",
......@@ -78,7 +95,28 @@ class UserAdmin(UserAdminBase):
"institutional_id_verified",
"editor_mode",)
}),
) + UserAdminBase.fieldsets[2:]
admin.site.register(User, UserAdmin)
*_fsets[2:],
)
ordering = ["-date_joined"]
def get_fieldsets(self, request, obj=None):
fieldsets = super().get_fieldsets(request, obj)
if request is not None and request.user.is_superuser:
return fieldsets
return tuple(
fields for fields in fieldsets
if "is_superuser" not in fields[1]["fields"]
and "is_staff" not in fields[1]["fields"]
and "user_permissions" not in fields[1]["fields"])
def get_list_display(self, request):
list_display = super().get_list_display(request)
if request is not None and request.user.is_superuser:
return list_display
return tuple(f for f in list_display if f != "is_staff")
def get_list_filter(self, request):
list_filter = super().get_list_filter(request)
if request is not None and request.user.is_superuser:
return list_filter
return tuple(f for f in list_filter if f != "is_staff")
from __future__ import annotations
from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import gettext_lazy as _
class AccountsConfig(AppConfig):
name = 'accounts'
name = "accounts"
default_auto_field = "django.db.models.BigAutoField"
# for translation of the name of "Accounts" app displayed in admin.
verbose_name = _("Accounts")
\ No newline at end of file
verbose_name = _("Accounts")
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.utils.timezone
import django.core.validators
import django.contrib.auth.models
class Migration(migrations.Migration):
dependencies = [
('auth', '0006_require_contenttypes_0002'),
]
operations = [
migrations.CreateModel(
name='User',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(null=True, verbose_name='last login', blank=True)),
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
('username', models.CharField(error_messages={b'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator(b'^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username')),
('first_name', models.CharField(max_length=30, verbose_name='first name', blank=True)),
('last_name', models.CharField(max_length=30, verbose_name='last name', blank=True)),
('email', models.EmailField(max_length=254, verbose_name='email address', blank=True)),
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
('groups', models.ManyToManyField(related_query_name=b'customtemp_user', related_name='customtemp_user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups')),
('user_permissions', models.ManyToManyField(related_query_name=b'customtemp_user', related_name='customtemp_user_set', to='auth.Permission', blank=True, help_text='Specific permissions for this user.', verbose_name='user permissions')),
],
options={
'abstract': False,
'verbose_name': 'user',
'verbose_name_plural': 'users',
},
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
]