Compare commits

...

103 Commits

Author SHA1 Message Date
Andreas Zweili c92d38bf62
Merge pull request #156 from Nebucatnetzer/dev
Update master
2023-08-28 20:45:59 +02:00
Andreas Zweili 08d0bf7963 Remove poetry from github actions 2023-08-28 20:18:17 +02:00
Andreas Zweili 0fb8faa4ec Only update the lock file when updating poetry depencies 2023-08-28 20:15:34 +02:00
Andreas Zweili 93731bf2c8 Readd poetry2nix
I have less problems with it than without.
2023-08-28 20:12:20 +02:00
Andreas Zweili 7e4a7ec125 Disable pylint messages for functions and classes 2023-08-28 17:33:14 +02:00
Andreas Zweili d238371a5c Whitelist pk and ip in pylint 2023-08-28 17:33:00 +02:00
Andreas Zweili 99bf6a1f58 Add env variable for LD_LIBRARY_PATH 2023-08-28 17:32:34 +02:00
Andreas Zweili 3a462ec1c3 Update dependencies 2023-08-28 17:12:14 +02:00
Andreas Zweili 60f2e97ad7
Merge pull request #152 from Nebucatnetzer/poetry
Remove poetry2nix
2023-08-28 17:06:09 +02:00
Andreas Zweili b92ef58c82 Remove poetry2nix 2023-08-28 17:01:22 +02:00
Andreas Zweili 796c8f3744
Merge pull request #151 from Nebucatnetzer/revert-150-pdm
Revert "Pdm"
2023-08-27 19:06:17 +02:00
Andreas Zweili 8181db93ea
Revert "Pdm" 2023-08-27 19:06:06 +02:00
Andreas Zweili e5012bc0f8
Merge pull request #150 from Nebucatnetzer/pdm
Pdm
2023-08-27 19:03:03 +02:00
Andreas Zweili ad799de420 Remove checks for the moment 2023-08-27 18:59:58 +02:00
Andreas Zweili 9fcb2a1a39 Add shfmt config 2023-08-27 18:59:58 +02:00
Andreas Zweili 45a2d9ed22 Replace poetry with pdm
In addition we stop using Nix managing the Python dependencies. It just takes
too much time to again and again fix the various problems.
2023-08-27 18:59:58 +02:00
Andreas Zweili 46b0c579e1 Merge branch 'dev' of github.com:Nebucatnetzer/network_inventory into dev 2023-08-27 12:27:08 +02:00
Andreas Zweili 48a484545b Update flake 2023-08-27 12:26:59 +02:00
Andreas Zweili 1da451dff5 Open the webserver URL in the default browser 2023-08-03 11:12:35 +02:00
Andreas Zweili 26a0f86786 Create the poetry venv in the project directory 2023-07-24 09:51:32 +02:00
Andreas Zweili 69e5a5bb16 Refactor internal functions 2023-07-22 18:27:33 +02:00
Andreas Zweili 844a2ff694 Add descriptions 2023-07-22 18:24:48 +02:00
Andreas Zweili 29ed447c5e Add dev stop command 2023-07-22 17:24:15 +02:00
Andreas Zweili c52bc7c382 Allow IP 0.0.0.0 2023-07-22 16:40:59 +02:00
Andreas Zweili ea33c3ffea Show URL to webserver 2023-07-22 16:37:49 +02:00
Andreas Zweili f2912d7655 Make dev.sh more flexible 2023-07-22 16:29:34 +02:00
Andreas Zweili 37b18a0536 Stop watching dev.sh
Direnv doesn't properly load the changes.
2023-07-22 16:28:55 +02:00
Andreas Zweili 5a43a97628 Reload the environment when dev.sh changes 2023-07-22 15:59:00 +02:00
Andreas Zweili 8bf4ee6bbe Randomize the ports
This should help with port colitions when running multiple projects at once.
2023-07-22 15:58:26 +02:00
Andreas Zweili 86e0ae7c5b Revert DB tests 2023-07-20 21:59:46 +02:00
Andreas Zweili 7930eef582 Start the DB for tests 2023-07-20 21:25:05 +02:00
Andreas Zweili 64bbd528ae Revert "Revert "Try to use the db in tests""
This reverts commit 757e1da5cb.
2023-07-20 21:06:54 +02:00
Andreas Zweili 1a4c75c7aa Remove unused docker settings 2023-07-18 21:44:35 +02:00
Andreas Zweili 4217774c13 Connect through socket to DB 2023-07-18 21:43:42 +02:00
Andreas Zweili 3734f26947
Merge pull request #148 from Nebucatnetzer/overmind
Overmind
2023-07-18 21:00:38 +02:00
Andreas Zweili df8e5914bc Rebuild the datbase if its config files gets removed 2023-07-18 20:59:09 +02:00
Andreas Zweili e36dd53c6b Add a sleep of 2 seconds
The database needs some time to shut down
2023-07-18 20:58:49 +02:00
Andreas Zweili 986434de8d Load the inventory fixtures 2023-07-18 20:58:39 +02:00
Andreas Zweili ceda8d419b Remove one object per line
Makes it easier to change
2023-07-18 20:47:24 +02:00
Andreas Zweili 04e1831d02 Replace cleanall with git clean -xdf 2023-07-18 20:44:01 +02:00
Andreas Zweili a623062fb5 Remove the database when cleaning 2023-07-18 20:41:12 +02:00
Andreas Zweili 42965ba283 Rename second_run to first_run 2023-07-18 20:40:58 +02:00
Andreas Zweili 120b86fa9a Reformat 2023-07-18 20:22:32 +02:00
Andreas Zweili 9153239c79 Extend README 2023-07-18 20:21:55 +02:00
Andreas Zweili 757e1da5cb Revert "Try to use the db in tests"
This reverts commit 10c221ec8b.
2023-07-14 18:49:37 +02:00
Andreas Zweili 10c221ec8b Try to use the db in tests 2023-07-14 18:17:21 +02:00
Andreas Zweili 3860b94c0b Create a django database 2023-07-14 14:34:56 +02:00
Andreas Zweili 05c61e275a Use the DB with the local username 2023-07-14 14:27:32 +02:00
Andreas Zweili 2e781b2cd1 Remove the password for local settings 2023-07-14 14:24:22 +02:00
Andreas Zweili 1551150ede Move the overmind quit command ouf of the if clause 2023-07-14 14:20:08 +02:00
Andreas Zweili 64ddd7ef66 Use the current user for DB access 2023-07-14 14:15:11 +02:00
Andreas Zweili cec752b998 Listen on localhost with postgresql 2023-07-14 14:15:01 +02:00
Andreas Zweili 0b61c4a23f Clean up the clean tasks 2023-07-13 23:48:59 +02:00
Andreas Zweili 311f2313e4 Rewrite setup to use overmind 2023-07-13 23:48:43 +02:00
Andreas Zweili f82c2bcc23 Make test runs work from any directory 2023-07-13 23:02:56 +02:00
Andreas Zweili dd6603992d Start the services with overmind 2023-07-13 22:25:50 +02:00
Andreas Zweili 3b04e8337e Remove the .direnv directory when cleaning 2023-07-13 22:22:23 +02:00
Andreas Zweili 4863b71bce Build the .venv into a hidden folder 2023-07-13 22:22:03 +02:00
Andreas Zweili 07a8d8cb33 Remove docker-compose-development.yml 2023-07-13 22:08:53 +02:00
Andreas Zweili 8af2b25042 Run tests in a RAM DB 2023-07-13 22:07:32 +02:00
Andreas Zweili 73df8d406a Add a Procfile 2023-07-13 22:07:23 +02:00
Andreas Zweili 9ba7625c50 Remove gnumake 2023-07-13 21:07:40 +02:00
Andreas Zweili 1c30e6ddb0 Remove docker-compose for development 2023-07-12 23:17:26 +02:00
Andreas Zweili e4b064b830 Extend direnv to create postgres dir 2023-07-12 23:16:32 +02:00
Andreas Zweili 0eea48d727 Add postgresql 15 2023-07-12 22:53:14 +02:00
Andreas Zweili c12e7f6b86 Add overmind 2023-07-12 22:53:07 +02:00
Andreas Zweili 0258363334 Merge branch 'execnet' into dev 2023-07-12 21:24:17 +02:00
Andreas Zweili 1d61db65f0 Update execnet overrides in upstream 2023-07-12 21:23:18 +02:00
Andreas Zweili f423680533 Remove unused variables 2023-07-11 21:37:53 +02:00
Andreas Zweili 3ded676db9 Try to update execnet 2023-07-11 21:11:30 +02:00
Andreas Zweili 6f6c31cf2d
Merge pull request #142 from Nebucatnetzer/dependabot/pip/django-filter-23.2
Bump django-filter from 22.1 to 23.2
2023-07-11 11:39:37 +02:00
Andreas Zweili 5f0b5cf199 Update flake 2023-07-10 21:46:36 +02:00
dependabot[bot] fd3d342cfe
Bump django-filter from 22.1 to 23.2
Bumps [django-filter](https://github.com/carltongibson/django-filter) from 22.1 to 23.2.
- [Release notes](https://github.com/carltongibson/django-filter/releases)
- [Changelog](https://github.com/carltongibson/django-filter/blob/main/CHANGES.rst)
- [Commits](https://github.com/carltongibson/django-filter/compare/22.1...23.2)

---
updated-dependencies:
- dependency-name: django-filter
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-07-10 17:43:54 +00:00
Andreas Zweili b05c6c81d6
Merge pull request #145 from Nebucatnetzer/mypy
Mypy
2023-07-10 19:42:23 +02:00
Andreas Zweili 83c1fbd93e Make sure that we return a string 2023-07-10 19:10:19 +02:00
Andreas Zweili ce3f748f33 Disable linting for nested_admin 2023-07-10 19:10:07 +02:00
Andreas Zweili fb7b9e7208 Correct setup for pylint 2023-07-10 19:09:37 +02:00
Andreas Zweili f6cb0f6873 Update dependencies 2023-07-10 17:38:04 +02:00
Andreas Zweili ca30519384 Remove unused dev packages 2023-07-10 17:36:04 +02:00
Andreas Zweili 729cf08cea Use default line length of black 2023-07-10 16:47:01 +02:00
Andreas Zweili 46df3a73b0
Update tests.yml 2023-07-10 09:36:23 +02:00
Andreas Zweili c72d1e66cc
Update publish.yml 2023-07-10 09:36:09 +02:00
Andreas Zweili bfb5c19931 Add pylint-django 2023-07-02 13:52:49 +02:00
Andreas Zweili b3c1f10718 Ignore types for DeleteView
This is caused by a bug:
https://github.com/typeddjango/django-stubs/issues/1227
2023-07-02 13:43:43 +02:00
Andreas Zweili c09184686b Add mypy django plugin 2023-07-02 13:42:54 +02:00
Andreas Zweili 6d1611bafa Correct the command to run mypy in flake 2023-07-02 13:42:42 +02:00
Andreas Zweili f7204f43d0 Add mypy 2023-07-02 13:11:04 +02:00
Andreas Zweili 89e5d6a47a Update flake 2023-06-25 15:13:22 +02:00
Andreas Zweili 41876ad29d Add python-lsp-server 2023-06-25 15:06:40 +02:00
Andreas Zweili 8665808f9e Disable the keyring
I don't really need it at the moment and there is a bug with poetry.
https://github.com/python-poetry/poetry/issues/5250
https://github.com/python-poetry/poetry/issues/1917
2023-06-25 15:05:38 +02:00
Andreas Zweili 91469e180f URLs should be strings 2023-06-21 21:22:39 +02:00
Andreas Zweili c7313fb63e
Update install-nix-action 2023-04-24 13:30:24 +02:00
Andreas Zweili 675eb4ff95 Update packages 2023-04-24 13:18:25 +02:00
Andreas Zweili f83e91efa6 Fix the clean command 2023-04-24 13:16:44 +02:00
Andreas Zweili ccfc1ea66c Update poetry 2023-03-07 21:53:18 +01:00
Andreas Zweili 9011d7a730 Add a check command 2023-03-07 21:53:03 +01:00
Andreas Zweili 559a88367a Add an update command 2023-03-07 21:37:17 +01:00
Andreas Zweili b072ee441f Update flake 2023-03-07 21:28:28 +01:00
Andreas Zweili 5c9fcd22f1 Correct the development URL 2023-03-07 21:12:17 +01:00
Andreas Zweili ed35a4913a
Merge pull request #112 from Nebucatnetzer/update-deps
Update dependencies
2023-01-29 13:02:16 +01:00
Andreas Zweili de219f7d62 Update dependencies 2023-01-29 12:55:38 +01:00
Andreas Zweili 59c949c9c0
Merge pull request #106 from Nebucatnetzer/dev
Remove unused package
2022-12-28 13:50:09 +01:00
Andreas Zweili 84c2516d24 Remove unused package 2022-12-28 13:43:25 +01:00
66 changed files with 1588 additions and 1539 deletions

View File

@ -1,4 +1,9 @@
[paths]
source = ${PROJECT_DIR-default .}/src/
[html]
directory = ${PROJECT_DIR-default .}/htmlcov
[run]
data_file = ${PROJECT_DIR-default .}/.coverage
omit =
*apps.py,
*migrations/*,

7
.editorconfig Normal file
View File

@ -0,0 +1,7 @@
[*.sh]
indent_style = space
indent_size = 4
[.envrc]
indent_style = space
indent_size = 4

46
.envrc
View File

@ -1,2 +1,48 @@
use flake
eval "$shellHook"
layout_postgres() {
export PGDATA="$(direnv_layout_dir)/postgres"
export PGHOST="$PGDATA"
if [[ ! -d "$PGDATA" ]]; then
initdb
echo -e "listen_addresses = 'localhost'\nunix_socket_directories = '$PGHOST'" >>"$PGDATA/postgresql.conf"
echo "CREATE DATABASE django;" | postgres --single -E postgres
fi
}
layout postgres
layout_poetry() {
PYPROJECT_TOML="${PYPROJECT_TOML:-pyproject.toml}"
if [[ ! -f "$PYPROJECT_TOML" ]]; then
log_status "No pyproject.toml found. Executing \`poetry init\` to create a \`$PYPROJECT_TOML\` first."
poetry init
fi
if [[ -d ".venv" ]]; then
VIRTUAL_ENV="$(pwd)/.venv"
else
VIRTUAL_ENV=$(
poetry env info --path 2>/dev/null
true
)
fi
if [[ -z $VIRTUAL_ENV || ! -d $VIRTUAL_ENV ]]; then
log_status "No virtual environment exists. Executing \`poetry install\` to create one."
poetry install
VIRTUAL_ENV=$(poetry env info --path)
fi
PATH_add "$VIRTUAL_ENV/bin"
export POETRY_ACTIVE=1
export VIRTUAL_ENV
}
if ! has nix; then
layout poetry
fi
export PROJECT_DIR=$(pwd)
export WEBPORT=$(($RANDOM + 1100))
export PGPORT=$(($WEBPORT + 100))
watch_file "$PGDATA/postgresql.conf"

View File

@ -1,8 +0,0 @@
[flake8]
exclude =
*migrations*,
__init__.py,
*cache*,
venv/,
src/manage.py,
src/network_inventory/settings/*

View File

@ -10,8 +10,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v18
- uses: cachix/cachix-action@v12
- uses: cachix/install-nix-action@v22
- uses: DeterminateSystems/magic-nix-cache-action@main
with:
name: networkinventory
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
@ -28,11 +28,8 @@ jobs:
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v18
- uses: cachix/cachix-action@v12
with:
name: networkinventory
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- uses: cachix/install-nix-action@v22
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Buid container
run: |
nix build .#container

View File

@ -11,11 +11,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: cachix/install-nix-action@v18
- uses: cachix/cachix-action@v12
with:
name: networkinventory
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- uses: cachix/install-nix-action@v22
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Test
run: |
nix flake check -L -j auto
run: nix develop --command bash -c "dev test"
env:
PROJECT_DIR: ${{ github.workspace }}

2
.gitignore vendored
View File

@ -173,7 +173,7 @@ migrations/
.vscode/
.pytest_cache/
htmlcov/
.second_run
.first_run
/src/static
.idea/

2
Procfile Normal file
View File

@ -0,0 +1,2 @@
web: python ./src/manage.py runserver 0.0.0.0:$WEBPORT
db: postgres -p $PGPORT

View File

@ -14,26 +14,62 @@ inventory over my various servers and other network equipment.
## Development Setup
There are two ways to work on this project.
For the first one you will need to install the Nix package manager[^1].
Afterwards you can enter the development environment with `nix develop`.
There is currently only one supported way to work with this repository. You
will need a Linux system (WSL might work) onto wich you install the Nix package
manager with Flakes enabled[^1] and direnv[^3]. Afterwards you can enter the
development environment with `direnv allow`.
For the other way you have to install poetry[^2] and then run `poetry shell` to
enter the virtual environment.
[^1]: https://nixos.org/download.html
[^3]: https://direnv.net/
After you've entered the development environment with either method you can
start the development server with `dev run`. This will start a PostgreSQL
database running and start the Django development server.
_It will prompt you for your sudo password because it opens port 8000 in your
firewall. This is because I sometimes develope from my iPad on my notebook and
with this tweak I can access the dev server running on my notebook._
You can then access the project in the browser under the FQDN of your
computer. E.g. `http://mypc.domain.local:8000`.
In case you want a fresh start or remove the project you can just remove the
`.direnv` directory at the root of the project. All the data of the PostgreSQL
database is stored there together with the symlinks to the Nix store.
In case you want to tweak something these are the applications use do build the
development environment:
- Nix package manager
- direnv
- overmind[^4]
The `dev` command is a simple BASH script called `dev.sh` at the root of the
project.
[^4]: https://github.com/DarthSim/overmind
Run the `dev` command without an argument to see all options.
> Why aren't you using Docker/containers for development.
_I think containers have their uses but developing with them is in my opinion a
pain in the ass. You just can't easily interact with the tools inside the
container and you have to hack around to get your editor working with it.
In addition they aren't fully reproducable. Nix solves all of these
problems. Overmind then comes into play to orchestrate the few tasks that are
required to get a development environment up an running._
**Manual way**
The manual way you have to install poetry[^2] and then run `poetry shell` to
enter the virtual environment. You will then need a local PostgreSQL server or
modify the settings so that you can use your prefered database.
Please note that I will only use and test the first method.
[^1]: https://nixos.org/download.html
[^2]: https://python-poetry.org
After you've entered the development environment with either method you can
start the server. With the nix version you can start it with `dev run`. With
poetry `./dev.sh run`. This will start a PostgreSQL database running inside a
docker container and start the Django development server. You can then access
it in the browser under the FQDN of your computer. E.g. `mypc.domain.local`.
Run the `dev` command without an argument to see all options.
## Environment Variables
To customise the application in the Docker container you can use environment

154
dev.sh
View File

@ -1,15 +1,15 @@
#!/usr/bin/env bash
run () {
setup
find . -name __pycache__ -o -name "*.pyc" -delete
sudo iptables -I INPUT -p tcp --dport 8000 -j ACCEPT
python ./src/manage.py runserver 0.0.0.0:8000
# Helper functions not exposed to the user {
# Load example data
_init() {
python ./src/manage.py loaddata src/network_inventory.yaml
}
setup () {
docker-compose -f docker-compose-development.yml up -d
if [ -f .second_run ]; then
# Setup the database
_setup() {
overmind start -l db -D
if [ -f .direnv/first_run ]; then
sleep 2
python ./src/manage.py collectstatic --noinput
python ./src/manage.py makemigrations
@ -34,59 +34,129 @@ setup () {
python ./src/manage.py loaddata nets
python ./src/manage.py loaddata softwares
python ./src/manage.py shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@example.com', 'password')"
touch .second_run
_init
touch .direnv/first_run
fi
overmind quit
sleep 2
}
_open_url() {
if [[ ! -z "${DEFAULT_BROWSER}" ]]; then
$DEFAULT_BROWSER $url
elif type explorer.exe &>/dev/null; then
explorer.exe $url
fi
}
venv () {
nix build .#venv -o venv
_create_url() {
if [ -f /etc/wsl.conf ]; then
echo "http://localhost:$WEBPORT"
else
echo "http://$(hostname -f):$WEBPORT"
fi
}
#}
docker (){
nix build && docker load < result && docker run --rm -ti network-inventory:latest
# Main tasks start
declare -A tasks
declare -A descriptions
run() {
_setup
find . -name __pycache__ -o -name "*.pyc" -delete
url=$(_create_url)
sudo iptables -I INPUT -p tcp --dport $WEBPORT -j ACCEPT
overmind start -D
printf "\n---\n webserver: $url\n---\n"
_open_url $url
}
descriptions["run"]="Start the webserver."
tasks["run"]=run
descriptions["start"]="Alias for run."
tasks["start"]=run
clean () {
docker-compose -f docker-compose-development.yml down -v
stop() {
overmind quit
}
descriptions["stop"]="Stop the webserver and DB."
tasks["stop"]=stop
venv() {
nix build .#venv -o .venv
}
descriptions["venv"]="Build a pseudo venv that editors like VS Code can use."
tasks["venv"]=venv
build-container() {
nix build && docker load <result
}
descriptions["build-container"]="Build and load OCI container."
tasks["build-container"]=build-container
clean() {
find . \( -name __pycache__ -o -name "*.pyc" \) -delete
rm -rf htmlcov/
rm -f */migrations/0*.py
rm .second_run
rm -f .direnv/first_run
rm -f src/*/migrations/0*.py
rm -rf .direnv/postgres/
}
descriptions["clean"]="Reset the project to a fresh state including the database."
tasks["clean"]=clean
cleanall () {
clean
docker-compose -f docker-compose-development.yml down -v --rmi local
rm -r .venv
cleanall() {
git clean -xdf
}
descriptions["cleanall"]="Completly remove any files which are not checked into git."
tasks["cleanall"]=cleanall
init () {
python ./src/manage.py loaddata network_inventory.yaml
}
debug () {
debug() {
pytest --pdb --nomigrations --cov=. --cov-report=html ./src/
}
descriptions["debug"]="Run the tests and drop into the debugger on failure."
tasks["debug"]=debug
test (){
nix flake check
lint() {
echo "Running pylint"
pylint \
--rc-file="$PROJECT_DIR/pyproject.toml" \
-j 0 \
-E "$PROJECT_DIR/src"
echo "Running mypy"
mypy --config-file="$PROJECT_DIR/pyproject.toml" "$PROJECT_DIR/src"
}
descriptions["lint"]="Run the linters against the src directory."
tasks["lint"]=lint
tasks=("clean" "cleanall" "debug" "docker" "run" "test" "venv")
test() {
DJANGO_SETTINGS_MODULE=network_inventory.settings.ram_test pytest \
-nauto \
--nomigrations \
--cov-config="$PROJECT_DIR/.coveragerc" \
--cov-report=html \
"$PROJECT_DIR/src"
}
descriptions["test"]="Run the tests in the RAM DB and write a coverage report."
tasks["test"]=test
update() {
poetry update --lock
}
descriptions["update"]="Update the dependencies."
tasks["update"]=update
# only one task at a time
if [ $# != 1 ]; then
echo "usage: $0 <task_name>"
echo "All tasks: ${tasks[@]}"
printf "usage: dev <task_name>\n\n"
for task in "${!tasks[@]}"; do
echo "$task - ${descriptions[$task]}"
done
else
# Check if task is available
if [[ -v "tasks[$1]" ]]; then
${tasks["$1"]}
else
echo "Task not found."
fi
fi
case $1 in
"${tasks[0]}") clean;;
"${tasks[1]}") cleanall;;
"${tasks[2]}") debug;;
"${tasks[3]}") docker;;
"${tasks[4]}") run;;
"${tasks[5]}") test;;
"${tasks[6]}") venv;;
esac

View File

@ -1,15 +0,0 @@
version: '3'
volumes:
db_data:
services:
db:
image: postgres
environment:
- POSTGRES_DB=network_inventory
- POSTGRES_PASSWORD=password
volumes:
- db_data:/var/lib/postgresql/data/
ports:
- "5432:5432"

View File

@ -1,12 +1,15 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"lastModified": 1687709756,
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
"type": "github"
},
"original": {
@ -16,12 +19,15 @@
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"lastModified": 1689068808,
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github"
},
"original": {
@ -30,13 +36,34 @@
"type": "github"
}
},
"nix-github-actions": {
"inputs": {
"nixpkgs": [
"poetry2nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1688870561,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1670242877,
"narHash": "sha256-jBLh7dRHnbfvPPA9znOC6oQfKrCPJ0El8Zoe0BqnCjQ=",
"lastModified": 1688918189,
"narHash": "sha256-f8ZlJ67LgEUDnN7ZsAyd1/Fyby1VdOXWg4XY/irSGrQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "6e51c97f1c849efdfd4f3b78a4870e6aa2da4198",
"rev": "408c0e8c15a1c9cf5c3226931b6f283c9867c484",
"type": "github"
},
"original": {
@ -49,16 +76,17 @@
"poetry2nix": {
"inputs": {
"flake-utils": "flake-utils_2",
"nix-github-actions": "nix-github-actions",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1670326426,
"narHash": "sha256-I5IscrjGuCbvpFIRoiappUwBBOq8OODvGLkapnn/ECA=",
"lastModified": 1693051011,
"narHash": "sha256-HNbuVCS/Fnl1YZOjBk9/MlIem+wM8fvIzTH0CVQrLSQ=",
"owner": "nix-community",
"repo": "poetry2nix",
"rev": "293dd5c31167540193bf2b66cec636eecd1fc788",
"rev": "5b3a5151cf212021ff8d424f215fb030e4ff2837",
"type": "github"
},
"original": {
@ -73,6 +101,36 @@
"nixpkgs": "nixpkgs",
"poetry2nix": "poetry2nix"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_2": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",

View File

@ -1,8 +1,8 @@
{
description = "A Python API for various tools I use at work.";
inputs = {
nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
flake-utils.url = github:numtide/flake-utils;
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
poetry2nix = {
url = "github:nix-community/poetry2nix";
inputs.nixpkgs.follows = "nixpkgs";
@ -45,31 +45,19 @@
rec {
devShells.default = pkgs.mkShell {
buildInputs = [
pkgs.gnumake
pkgs.inventoryDevEnv
pkgs.poetry
pkgs.python310Packages.pip
pkgs.overmind
pkgs.postgresql_15
(pkgs.writeScriptBin "dev" "${builtins.readFile ./dev.sh}")
];
PYTHON_KEYRING_BACKEND = "keyring.backends.fail.Keyring";
shellHook = ''
export DJANGO_SETTINGS_MODULE=network_inventory.settings.local
'';
};
checks = {
lint = pkgs.stdenv.mkDerivation {
dontPatch = true;
dontConfigure = true;
dontBuild = true;
dontInstall = true;
doCheck = true;
name = "lint";
src = ./.;
checkInputs = [ pkgs.inventoryDevEnv ];
checkPhase = ''
mkdir -p $out
flake8 . --count --show-source --statistics
'';
};
tests = pkgs.stdenv.mkDerivation {
dontPatch = true;
dontConfigure = true;
@ -82,10 +70,10 @@
checkPhase = ''
mkdir -p $out
pytest --ds=network_inventory.settings.ram_test \
-nauto \
--nomigrations \
--cov=./src \
./src
-nauto \
--nomigrations \
--cov=./src \
./src
'';
};
};
@ -102,7 +90,7 @@
pkgs.coreutils
inventory
(pkgs.writeShellScriptBin "start-inventory" ''
if [ -f .second_run ]; then
if [ -f .first_run ]; then
sleep 2
${pkgs.inventoryEnv}/bin/django-admin collectstatic --noinput
${pkgs.inventoryEnv}/bin/django-admin makemigrations
@ -127,7 +115,7 @@
${pkgs.inventoryEnv}/bin/django-admin loaddata nets
${pkgs.inventoryEnv}/bin/django-admin loaddata softwares
${pkgs.inventoryEnv}/bin/django-admin shell -c "from django.contrib.auth import get_user_model; User = get_user_model(); User.objects.create_superuser('admin', 'admin@example.com', 'password')"
touch .second_run
touch .first_run
fi
${pkgs.inventoryEnv}/bin/gunicorn network_inventory.wsgi:application --reload --bind 0.0.0.0:8000 --workers 3
'')

2106
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,18 @@
[tool.black]
line-length = 79
[tool.pylint]
max-line-length = 88
load-plugins = [
"pylint_django",
]
good-names = [
"pk",
"ip",
]
[tool.pylint."MESSAGES CONTROL"]
disable = [
"missing-function-docstring",
"missing-class-docstring",
]
[tool.poetry]
name = "network_inventory"
@ -11,37 +24,73 @@ packages = [
{ include = "src" },
]
[tool.mypy]
exclude = [
"tests/",
]
plugins = ["mypy_django_plugin.main"]
mypy_path = "./src"
# Start off with these
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
# Getting these passing should be easy
strict_equality = true
strict_concatenate = true
# Strongly recommend enabling this one as soon as you can
#check_untyped_defs = true
# These shouldn't be too much additional work, but may be tricky to
# get passing if you use a lot of untyped libraries
#disallow_subclassing_any = true
#disallow_untyped_decorators = true
#disallow_any_generics = true
[tool.django-stubs]
django_settings_module = "network_inventory.settings.local"
[[tool.mypy.overrides]]
module = [
"nested_admin.*",
"django_tables2.*",
"floppyforms.*",
"django_filters.*",
"crispy_forms.*",
"mixer.*",
"guardian.*",
]
ignore_missing_imports = true
[tool.poetry.group.main.dependencies]
python = "^3.9"
Django = "^4.1.3"
django-crispy-forms = "^1.14.0"
django-filter = "^22.1"
django-filter = "^23.2"
django-floppyforms = "^1.9.0"
django-guardian = "^2.4.0"
django-htmx = "^1.13.0"
django-model-utils = "^4.2.0"
django-nested-admin = "^4.0.2"
django-tables2 = "^2.4.1"
django-tables2 = "^2.4.1,<2.6.0"
gunicorn = "^20.1.0"
psycopg2-binary = "^2.9.5"
PyYAML = "^6.0"
[tool.poetry.group.dev.dependencies]
autopep8 = "^2.0.0"
black = "^22.10.0"
coverage = "^6.5.0"
flake8 = "^6.0.0"
jedi = "^0.18.2"
mixer = "^7.2.2"
pep8 = "^1.7.1"
pylint = "^2.15.8"
pytest = "^7.2.0"
pytest-cov = "^4.0.0"
pytest-django = "^4.5.2"
pytest-forked = "^1.4.0"
pytest-xdist = "^3.1.0"
rope = "^1.5.1"
yapf = "^0.32.0"
python-lsp-server = "^1.7.3"
mypy = "^1.4.1"
django-stubs = "^4.2.3"
pylint-django = "^2.5.3"
[build-system]
requires = ["poetry-core>=1.0.0"]

View File

@ -11,8 +11,6 @@ def backup_view_permission(old_fuction):
if user.has_perm("customers.view_customer", backup.computer.customer):
return old_fuction(request, pk)
else:
return HttpResponseForbidden(
"You're not allowed to access this device."
)
return HttpResponseForbidden("You're not allowed to access this device.")
return new_function

View File

@ -20,18 +20,12 @@ class Backup(models.Model):
computer = models.ForeignKey(
Computer, related_name="source_computer", on_delete=models.CASCADE
)
method = models.ForeignKey(
BackupMethod, models.SET_NULL, blank=True, null=True
)
software = models.ForeignKey(
Software, models.SET_NULL, blank=True, null=True
)
method = models.ForeignKey(BackupMethod, models.SET_NULL, blank=True, null=True)
software = models.ForeignKey(Software, models.SET_NULL, blank=True, null=True)
source_path = models.CharField(max_length=200, blank=True)
exec_time = models.TimeField(null=True, blank=True)
exec_days = models.ManyToManyField(Weekday, blank=True)
target_device = models.ManyToManyField(
Computer, through="TargetDevice", blank=True
)
target_device = models.ManyToManyField(Computer, through="TargetDevice", blank=True)
class Meta:
ordering = ["name"]
@ -50,9 +44,7 @@ class Backup(models.Model):
class TargetDevice(models.Model):
device = models.ForeignKey(
Computer, models.SET_NULL, blank=True, null=True
)
device = models.ForeignKey(Computer, models.SET_NULL, blank=True, null=True)
backup = models.ForeignKey(Backup, on_delete=models.CASCADE)
target_path = models.CharField(max_length=200, blank=True)

View File

@ -53,9 +53,7 @@ def test_backup_detail_view_with_target_device(create_admin_user):
software=mixer.SELECT,
method=mixer.SELECT,
)
mixer.blend(
"backups.TargetDevice", device=target_computer, backup=mixer.SELECT
)
mixer.blend("backups.TargetDevice", device=target_computer, backup=mixer.SELECT)
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/backup/" + str(backup.id) + "/")
@ -79,9 +77,7 @@ def test_backup_detail_view_with_notification(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/backup/" + str(backup.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, notification
)
assert response.status_code == 200 and helper.in_content(response, notification)
def test_backup_detail_view_with_day_relation(create_admin_user):

View File

@ -23,9 +23,7 @@ def test_customer_backup_table(create_admin_user):
computer = mixer.blend("computers.Computer", customer=customer)
backup = mixer.blend("backups.Backup", computer=computer)
response = client.get("/customer/" + str(customer.id) + "/backups/")
assert response.status_code == 200 and helper.in_content(
response, backup.name
)
assert response.status_code == 200 and helper.in_content(response, backup.name)
def test_customer_backup_table_no_backup(create_admin_user):

View File

@ -3,9 +3,7 @@ from django.urls import path
from . import views
urlpatterns = [
path(
"customer/<int:pk>/backups/", views.backups_table_view, name="backups"
),
path("customer/<int:pk>/backups/", views.backups_table_view, name="backups"),
path("backup/<int:pk>/", views.backup_detail_view, name="backup"),
path(
"create/backup-for-computer/<int:pk>/",

View File

@ -47,7 +47,7 @@ def backup_detail_view(request, pk):
class BackupCreateView(LoginRequiredMixin, CreateView):
model = Backup
template_name = "backups/backup_create.html"
fields = "__all__"
fields = "__all__" # type: ignore
def get_success_url(self):
return reverse("computer", args=(self.computer.pk,))
@ -63,7 +63,7 @@ class BackupCreateView(LoginRequiredMixin, CreateView):
}
class BackupDeleteView(LoginRequiredMixin, DeleteView):
class BackupDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Backup
template_name = "backups/backup_confirm_delete.html"
@ -71,7 +71,7 @@ class BackupDeleteView(LoginRequiredMixin, DeleteView):
return reverse("computer", args=(self.object.computer.pk,))
class BackupDeleteFromTableView(LoginRequiredMixin, DeleteView):
class BackupDeleteFromTableView(LoginRequiredMixin, DeleteView): # type: ignore
model = Backup
template_name = "backups/backup_confirm_delete.html"

View File

@ -26,62 +26,64 @@ from .models import (
)
class SoftwareInLine(nested_admin.NestedStackedInline):
class SoftwareInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = ComputerSoftwareRelation
extra = 0
verbose_name_plural = "Software"
class RamInLine(nested_admin.NestedStackedInline):
class RamInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = ComputerRamRelation
extra = 0
verbose_name_plural = "RAM Modules"
class DiskInLine(nested_admin.NestedStackedInline):
class DiskInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = ComputerDiskRelation
extra = 0
verbose_name_plural = "Disks"
class DisksInRaidInLine(nested_admin.NestedStackedInline):
class DisksInRaidInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = DisksInRaid
extra = 0
verbose_name_plural = "Disks in RAID"
class CpusInLine(nested_admin.NestedStackedInline):
class CpusInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = ComputerCpuRelation
extra = 0
verbose_name_plural = "CPUs"
class GpusInLine(nested_admin.NestedStackedInline):
class GpusInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = ComputerGpuRelation
extra = 0
verbose_name_plural = "GPUs"
class RaidInLine(nested_admin.NestedStackedInline):
class RaidInLine(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = Raid
extra = 0
verbose_name_plural = "RAID"
inlines = (DisksInRaidInLine,)
class DeviceInNetInline(nested_admin.NestedStackedInline):
class DeviceInNetInline(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = DeviceInNet
extra = 0
verbose_name_plural = "Nets"
class LicenseWithComputerInLine(nested_admin.NestedStackedInline):
class LicenseWithComputerInLine(
nested_admin.NestedStackedInline
): # pylint: disable=no-member
model = LicenseWithComputer
extra = 0
verbose_name_plural = "Licenses"
class ComputerAdmin(nested_admin.NestedModelAdmin):
class ComputerAdmin(nested_admin.NestedModelAdmin): # pylint: disable=no-member
list_display = ("name", "host")
inlines = (
SoftwareInLine,

View File

@ -41,15 +41,9 @@ class ComputerUpdateForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(ComputerUpdateForm, self).__init__(*args, **kwargs)
customers = utils.objects_for_allowed_customers(
Customer, user=request.user
)
locations = utils.objects_for_allowed_customers(
Location, user=request.user
)
hosts = utils.objects_for_allowed_customers(
Computer, user=request.user
)
customers = utils.objects_for_allowed_customers(Customer, user=request.user)
locations = utils.objects_for_allowed_customers(Location, user=request.user)
hosts = utils.objects_for_allowed_customers(Computer, user=request.user)
users = utils.objects_for_allowed_customers(User, user=request.user)
self.fields["customer"].queryset = customers
self.fields["location"].queryset = locations

View File

@ -23,12 +23,8 @@ class Computer(Device):
ram = models.ManyToManyField(Ram, through="ComputerRamRelation")
gpu = models.ManyToManyField(Gpu, through="ComputerGpuRelation")
disks = models.ManyToManyField(Disk, through="ComputerDiskRelation")
software = models.ManyToManyField(
Software, through="ComputerSoftwareRelation"
)
host = models.ForeignKey(
"self", null=True, blank=True, on_delete=models.CASCADE
)
software = models.ManyToManyField(Software, through="ComputerSoftwareRelation")
host = models.ForeignKey("self", null=True, blank=True, on_delete=models.CASCADE)
allocated_space = models.IntegerField(null=True, blank=True)
def __str__(self):

View File

@ -12,9 +12,7 @@ class RaidType(Category):
class Raid(models.Model):
usable_space = models.IntegerField(blank=True, null=True)
raid_type = models.ForeignKey(
RaidType, models.SET_NULL, blank=True, null=True
)
raid_type = models.ForeignKey(RaidType, models.SET_NULL, blank=True, null=True)
computer = models.ForeignKey(Computer, on_delete=models.CASCADE)
def __str__(self):

View File

@ -15,15 +15,11 @@ def test_computer_detail_view_not_logged_in():
def test_computer_detail_view(create_admin_user):
create_admin_user()
computer = mixer.blend(
"computers.Computer", customer=mixer.SELECT, os=mixer.SELECT
)
computer = mixer.blend("computers.Computer", customer=mixer.SELECT, os=mixer.SELECT)
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/computer/" + str(computer.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, computer
)
assert response.status_code == 200 and helper.in_content(response, computer)
def test_computer_detail_view_not_found(create_admin_user):
@ -43,9 +39,7 @@ def test_computer_detail_view_ram_relation(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/computer/" + str(computer.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, "RAM Modules:"
)
assert response.status_code == 200 and helper.in_content(response, "RAM Modules:")
def test_computer_detail_view_raid_relation(create_admin_user):
@ -53,9 +47,7 @@ def test_computer_detail_view_raid_relation(create_admin_user):
computer = mixer.blend("computers.Computer", customer=mixer.SELECT)
raid_type = mixer.blend("computers.RaidType")
disk = mixer.blend("computers.Disk")
raid = mixer.blend(
"computers.Raid", computer=computer, raid_type=raid_type
)
raid = mixer.blend("computers.Raid", computer=computer, raid_type=raid_type)
mixer.blend("computers.DisksInRaid", raid=raid, disk=disk)
client = Client()
client.login(username="pharma-admin", password="password")

View File

@ -12,9 +12,7 @@ def test_computer_create_form(create_admin_user):
fixture = create_admin_user()
user = mixer.blend("core.InventoryUser", customer=fixture["customer"])
form = forms.ComputerCreateForm(user=user, data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
data = {"name": "pharma-pc1", "customer": 3}
form = forms.ComputerCreateForm(user=user, data=data)
@ -32,9 +30,7 @@ def test_computer_update_form(create_admin_user):
request = HttpRequest()
request.user = fixture["admin"]
form = forms.ComputerUpdateForm(request, data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
data = {"name": "pharma-pc1", "customer": 20356}
form = forms.ComputerUpdateForm(request, data=data)

View File

@ -27,6 +27,4 @@ def test_computer_list_view(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/computers/all/")
assert response.status_code == 200 and helper.in_content(
response, computer
)
assert response.status_code == 200 and helper.in_content(response, computer)

View File

@ -21,9 +21,7 @@ def test_customer_computer_table(create_admin_user):
client.login(username="pharma-admin", password="password")
computer = mixer.blend("computers.Computer", customer=mixer.SELECT)
response = client.get("/customer/" + str(customer.id) + "/computers/")
assert response.status_code == 200 and helper.in_content(
response, computer
)
assert response.status_code == 200 and helper.in_content(response, computer)
def test_customer_computer_table_no_computer(create_admin_user):

View File

@ -41,9 +41,7 @@ from .tables import ComputersTable
@login_required
def computer_detail_view(request, pk):
device = utils.get_object_with_view_permission(
Computer, user=request.user, pk=pk
)
device = utils.get_object_with_view_permission(Computer, user=request.user, pk=pk)
disks_relations = ComputerDiskRelation.objects.filter(computer=pk)
warranty_relations = Warranty.objects.filter(device=pk)
ram_relations = ComputerRamRelation.objects.filter(computer=pk)
@ -76,9 +74,7 @@ def computer_detail_view(request, pk):
@login_required
def computers_table_view(request, pk):
table = ComputersTable(
utils.get_objects_for_customer(
Computer, user=request.user, customer_pk=pk
)
utils.get_objects_for_customer(Computer, user=request.user, customer_pk=pk)
)
RequestConfig(request).configure(table)
return render(
@ -139,9 +135,7 @@ def computer_update_view(request, pk):
A view to create a customer.
"""
template_name = "computers/computer_update.html"
computer = utils.get_object_with_view_permission(
Computer, user=request.user, pk=pk
)
computer = utils.get_object_with_view_permission(Computer, user=request.user, pk=pk)
if request.method == "POST":
form = ComputerUpdateForm(request, request.POST, instance=computer)
if form.is_valid():
@ -152,7 +146,7 @@ def computer_update_view(request, pk):
return TemplateResponse(request, template_name, {"form": form})
class ComputerDeleteView(LoginRequiredMixin, DeleteView):
class ComputerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Computer
def get_success_url(self):
@ -178,7 +172,7 @@ class ComputerRamRelationCreateView(LoginRequiredMixin, CreateView):
}
class ComputerRamRelationDeleteView(LoginRequiredMixin, DeleteView):
class ComputerRamRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerRamRelation
template_name = "computers/relation_confirm_delete.html"
@ -205,7 +199,7 @@ class ComputerCpuRelationCreateView(LoginRequiredMixin, CreateView):
}
class ComputerCpuRelationDeleteView(LoginRequiredMixin, DeleteView):
class ComputerCpuRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerCpuRelation
template_name = "computers/relation_confirm_delete.html"
@ -232,7 +226,7 @@ class ComputerGpuRelationCreateView(LoginRequiredMixin, CreateView):
}
class ComputerGpuRelationDeleteView(LoginRequiredMixin, DeleteView):
class ComputerGpuRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerGpuRelation
template_name = "computers/relation_confirm_delete.html"
@ -259,7 +253,7 @@ class ComputerDiskRelationCreateView(LoginRequiredMixin, CreateView):
}
class ComputerDiskRelationDeleteView(LoginRequiredMixin, DeleteView):
class ComputerDiskRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerDiskRelation
template_name = "computers/relation_confirm_delete.html"
@ -286,7 +280,7 @@ class ComputerSoftwareRelationCreateView(LoginRequiredMixin, CreateView):
}
class ComputerSoftwareRelationDeleteView(LoginRequiredMixin, DeleteView):
class ComputerSoftwareRelationDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerSoftwareRelation
template_name = "computers/relation_confirm_delete.html"
@ -313,7 +307,7 @@ class RaidCreateView(LoginRequiredMixin, CreateView):
}
class RaidDeleteView(LoginRequiredMixin, DeleteView):
class RaidDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Raid
template_name = "computers/relation_confirm_delete.html"

View File

@ -22,9 +22,7 @@ def django_db_setup(django_db_setup, django_db_blocker):
def create_admin_user():
def _create_admin_user():
User = get_user_model()
admin = User.objects.create_user(
"pharma-admin", "admin@pharma.com", "password"
)
admin = User.objects.create_user("pharma-admin", "admin@pharma.com", "password")
customer = mixer.blend("customers.Customer")
group = Group.objects.create(name="Pharma Corp. Admin")
admin.groups.add(group)

View File

@ -15,9 +15,7 @@ def test_get_object_with_view_permission(create_admin_user):
fixture = create_admin_user()
customer = fixture["customer"]
admin = fixture["admin"]
object = utils.get_object_with_view_permission(
Customer, user=admin, pk=customer.id
)
object = utils.get_object_with_view_permission(Customer, user=admin, pk=customer.id)
assert object == customer
@ -26,9 +24,7 @@ def test_get_object_with_view_permission_device(create_admin_user):
customer = fixture["customer"]
admin = fixture["admin"]
device = mixer.blend(Device, customer=customer)
object = utils.get_object_with_view_permission(
Device, user=admin, pk=device.id
)
object = utils.get_object_with_view_permission(Device, user=admin, pk=device.id)
assert object == device
@ -37,9 +33,7 @@ def test_get_object_without_view_permission(create_admin_user):
customer = mixer.blend(Customer)
admin = fixture["admin"]
with pytest.raises(Http404):
utils.get_object_with_view_permission(
Customer, user=admin, pk=customer.id
)
utils.get_object_with_view_permission(Customer, user=admin, pk=customer.id)
def test_get_object_without_view_permission_device(create_admin_user):

View File

@ -16,9 +16,7 @@ def test_get_objects_for_customer_with_customer(create_admin_user):
customer = fixture["customer"]
admin = fixture["admin"]
with pytest.raises(Exception):
utils.get_objects_for_customer(
Customer, user=admin, customer_pk=customer.id
)
utils.get_objects_for_customer(Customer, user=admin, customer_pk=customer.id)
def test_get_objects_for_customer_device(create_admin_user):
@ -37,9 +35,7 @@ def test_get_all_objects_for_unallowed_customers(create_admin_user):
customer = mixer.blend(Customer)
admin = fixture["admin"]
with pytest.raises(Http404):
utils.get_objects_for_customer(
Customer, user=admin, customer_pk=customer.id
)
utils.get_objects_for_customer(Customer, user=admin, customer_pk=customer.id)
def test_get_all_objects_for_unallowed_customers_device(create_admin_user):
@ -48,6 +44,4 @@ def test_get_all_objects_for_unallowed_customers_device(create_admin_user):
admin = fixture["admin"]
mixer.blend(Device, customer=customer)
with pytest.raises(Http404):
utils.get_objects_for_customer(
Device, user=admin, customer_pk=customer.id
)
utils.get_objects_for_customer(Device, user=admin, customer_pk=customer.id)

View File

@ -34,9 +34,7 @@ def _get_customers(user):
user : django.contrib.auth.models.User
"""
return get_objects_for_user(
user, "customers.view_customer", klass=Customer
)
return get_objects_for_user(user, "customers.view_customer", klass=Customer)
def get_object_with_view_permission(model, user=None, pk=None):

View File

@ -10,8 +10,6 @@ def customer_view_permission(old_function):
if user.has_perm("customers.view_customer", customer):
return old_function(request, pk)
else:
return HttpResponseForbidden(
"You're not allowed to access this page."
)
return HttpResponseForbidden("You're not allowed to access this page.")
return new_function

View File

@ -43,4 +43,4 @@ class DummyLocation(models.Model):
location = models.ForeignKey(Location, on_delete=models.CASCADE)
def __str__(self):
return self.location
return self.location.name

View File

@ -6,9 +6,7 @@ from core.tables import CoreTable
class CustomersTable(CoreTable):
name = tables.LinkColumn("customer", args=[A("pk")])
nets = tables.LinkColumn(
"nets", text="Nets", args=[A("pk")], orderable=False
)
nets = tables.LinkColumn("nets", text="Nets", args=[A("pk")], orderable=False)
computers = tables.LinkColumn(
"computers", text="Computers", args=[A("pk")], orderable=False
)
@ -21,12 +19,8 @@ class CustomersTable(CoreTable):
licenses = tables.LinkColumn(
"licenses", text="Licenses", args=[A("pk")], orderable=False
)
users = tables.LinkColumn(
"users", text="Users", args=[A("pk")], orderable=False
)
groups = tables.LinkColumn(
"groups", text="Groups", args=[A("pk")], orderable=False
)
users = tables.LinkColumn("users", text="Users", args=[A("pk")], orderable=False)
groups = tables.LinkColumn("groups", text="Groups", args=[A("pk")], orderable=False)
project_manager = tables.Column(verbose_name="Project Manager")
delete = tables.LinkColumn(
"customer_delete",

View File

@ -42,24 +42,12 @@ def test_customer_list_view(create_admin_user):
assert (
response.status_code == 200
and helper.in_content(response, customer)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/nets/"
)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/computers/"
)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/devices/"
)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/backups/"
)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/licenses/"
)
and helper.in_content(
response, "/customer/" + str(customer.id) + "/users/"
)
and helper.in_content(response, "/customer/" + str(customer.id) + "/nets/")
and helper.in_content(response, "/customer/" + str(customer.id) + "/computers/")
and helper.in_content(response, "/customer/" + str(customer.id) + "/devices/")
and helper.in_content(response, "/customer/" + str(customer.id) + "/backups/")
and helper.in_content(response, "/customer/" + str(customer.id) + "/licenses/")
and helper.in_content(response, "/customer/" + str(customer.id) + "/users/")
and helper.in_content(response, project_manager)
)
@ -75,41 +63,21 @@ def test_customer_list_view_multiple_customers(create_admin_user):
assert (
response.status_code == 200
and helper.in_content(response, customer1)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/nets/"
)
and helper.in_content(response, "/customer/" + str(customer1.id) + "/nets/")
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/computers/"
)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/devices/"
)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/backups/"
)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/licenses/"
)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/users/"
)
and helper.in_content(response, "/customer/" + str(customer1.id) + "/devices/")
and helper.in_content(response, "/customer/" + str(customer1.id) + "/backups/")
and helper.in_content(response, "/customer/" + str(customer1.id) + "/licenses/")
and helper.in_content(response, "/customer/" + str(customer1.id) + "/users/")
and helper.in_content(response, customer2)
and helper.in_content(
response, "/customer/" + str(customer2.id) + "/nets/"
)
and helper.in_content(response, "/customer/" + str(customer2.id) + "/nets/")
and helper.in_content(
response, "/customer/" + str(customer2.id) + "/computers/"
)
and helper.in_content(
response, "/customer/" + str(customer2.id) + "/devices/"
)
and helper.in_content(
response, "/customer/" + str(customer2.id) + "/backups/"
)
and helper.in_content(
response, "/customer/" + str(customer2.id) + "/licenses/"
)
and helper.in_content(
response, "/customer/" + str(customer1.id) + "/users/"
)
and helper.in_content(response, "/customer/" + str(customer2.id) + "/devices/")
and helper.in_content(response, "/customer/" + str(customer2.id) + "/backups/")
and helper.in_content(response, "/customer/" + str(customer2.id) + "/licenses/")
and helper.in_content(response, "/customer/" + str(customer1.id) + "/users/")
)

View File

@ -10,9 +10,7 @@ def test_location_form(create_admin_user):
fixture = create_admin_user()
user = fixture["admin"]
form = forms.LocationForm(user=user, data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
data = {"name": "Main Office", "customer": 3}
form = forms.LocationForm(user=user, data=data)

View File

@ -14,9 +14,7 @@ def test_load_htmx_create_location_view(create_admin_user):
client.login(username="pharma-admin", password="password")
url = "/create/location/"
response = client.get(url)
assert response.status_code == 200 and helper.in_content(
response, "Add Location"
)
assert response.status_code == 200 and helper.in_content(response, "Add Location")
def test_htmx_create_location_view(create_admin_user):
@ -25,9 +23,7 @@ def test_htmx_create_location_view(create_admin_user):
client.login(username="pharma-admin", password="password")
data = {"name": mixer.faker.name(), "save_location": 1}
response = client.post("/create/location/", data)
assert response.status_code == 200 and helper.in_content(
response, data["name"]
)
assert response.status_code == 200 and helper.in_content(response, data["name"])
def test_htmx_create_location_view_invalid_form(create_admin_user):

View File

@ -24,9 +24,7 @@ def customers_table_view(request):
customers = utils.objects_for_allowed_customers(Customer, request.user)
table = CustomersTable(customers)
RequestConfig(request).configure(table)
return render(
request, "customers/customer_list.html", {"customers": table}
)
return render(request, "customers/customer_list.html", {"customers": table})
@login_required
@ -44,23 +42,17 @@ def create_customer(request):
)
form = CustomerForm()
context = {"form": form}
return TemplateResponse(
request, "customers/partials/customer_create.html", context
)
return TemplateResponse(request, "customers/partials/customer_create.html", context)
@login_required
def customer_detail_view(request, pk):
customer = utils.get_object_with_view_permission(
Customer, user=request.user, pk=pk
)
customer = utils.get_object_with_view_permission(Customer, user=request.user, pk=pk)
context = {"customer": customer}
return TemplateResponse(
request, "customers/customer_details.html", context
)
return TemplateResponse(request, "customers/customer_details.html", context)
class CustomerDeleteView(LoginRequiredMixin, DeleteView):
class CustomerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Customer
def get_success_url(self):

View File

@ -12,13 +12,13 @@ from .models import (
)
class DeviceInNetInline(nested_admin.NestedStackedInline):
class DeviceInNetInline(nested_admin.NestedStackedInline): # pylint: disable=no-member
model = DeviceInNet
extra = 0
verbose_name_plural = "Nets"
class DeviceAdmin(nested_admin.NestedModelAdmin):
class DeviceAdmin(nested_admin.NestedModelAdmin): # pylint: disable=no-member
inlines = (DeviceInNetInline,)

View File

@ -11,8 +11,6 @@ def device_view_permission(old_function):
if user.has_perm("customers.view_customer", device.customer):
return old_function(request, pk)
else:
return HttpResponseForbidden(
"You're not allowed to access this device."
)
return HttpResponseForbidden("You're not allowed to access this device.")
return new_function

View File

@ -69,12 +69,8 @@ class DeviceUpdateForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(DeviceUpdateForm, self).__init__(*args, **kwargs)
customers = utils.objects_for_allowed_customers(
Customer, user=request.user
)
locations = utils.objects_for_allowed_customers(
Location, user=request.user
)
customers = utils.objects_for_allowed_customers(Customer, user=request.user)
locations = utils.objects_for_allowed_customers(Location, user=request.user)
users = utils.objects_for_allowed_customers(User, user=request.user)
self.fields["customer"].queryset = customers
self.fields["location"].queryset = locations

View File

@ -38,9 +38,7 @@ class DeviceCategory(Category):
class HardwareModel(models.Model):
name = models.CharField(max_length=50)
manufacturer = models.ForeignKey(
DeviceManufacturer, on_delete=models.CASCADE
)
manufacturer = models.ForeignKey(DeviceManufacturer, on_delete=models.CASCADE)
class Meta:
ordering = ["name"]
@ -56,19 +54,13 @@ class Device(models.Model):
category = models.ForeignKey(
DeviceCategory, on_delete=models.SET_NULL, null=True, blank=True
)
owner = models.ForeignKey(
Owner, on_delete=models.SET_NULL, null=True, blank=True
)
owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, null=True, blank=True)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
manufacturer = models.ForeignKey(
DeviceManufacturer, models.SET_NULL, null=True, blank=True
)
model = models.ForeignKey(
HardwareModel, models.SET_NULL, null=True, blank=True
)
location = models.ForeignKey(
Location, models.SET_NULL, null=True, blank=True
)
model = models.ForeignKey(HardwareModel, models.SET_NULL, null=True, blank=True)
location = models.ForeignKey(Location, models.SET_NULL, null=True, blank=True)
user = models.ForeignKey(User, models.SET_NULL, null=True, blank=True)
installation_date = models.DateField(null=True, blank=True)
net = models.ManyToManyField(Net, through="DeviceInNet")

View File

@ -16,9 +16,7 @@ class WarrantyType(Category):
class Warranty(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, blank=True
)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, blank=True)
device = models.ForeignKey(Device, on_delete=models.CASCADE)
valid_from = models.DateField()
valid_until = models.DateField()

View File

@ -12,9 +12,7 @@ def test_device_create_form(create_admin_user):
fixture = create_admin_user()
user = mixer.blend("core.InventoryUser", customer=fixture["customer"])
form = forms.DeviceCreateForm(user=user, data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
data = {"name": "pharma-device1", "customer": 3}
form = forms.DeviceCreateForm(user=user, data=data)
@ -32,9 +30,7 @@ def test_device_update_form(create_admin_user):
request = HttpRequest()
request.user = fixture["admin"]
form = forms.DeviceUpdateForm(request, data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
data = {"name": "pharma-device1", "customer": 3}
form = forms.DeviceUpdateForm(request, data=data)
@ -50,9 +46,7 @@ def test_device_update_form(create_admin_user):
def test_device_create_form_duplicate_device(create_admin_user):
fixture = create_admin_user()
user = mixer.blend("core.InventoryUser", customer=fixture["customer"])
mixer.blend(
"devices.Device", name="pharma-device1", customer=fixture["customer"]
)
mixer.blend("devices.Device", name="pharma-device1", customer=fixture["customer"])
data = {"name": "pharma-device1", "customer": fixture["customer"].id}
form = forms.DeviceCreateForm(user=user, data=data)
assert (

View File

@ -35,9 +35,7 @@ def test_load_device_update_view(create_admin_user):
client.login(username="pharma-admin", password="password")
device = mixer.blend("devices.Device", customer=mixer.SELECT)
response = client.get("/update/device/{}/".format(device.pk))
assert response.status_code == 200 and helper.in_content(
response, device.name
)
assert response.status_code == 200 and helper.in_content(response, device.name)
def test_device_update_view(create_admin_user):
@ -187,9 +185,7 @@ def test_device_in_net_update_view(create_admin_user):
"mac_address": "",
"ip_status": "1",
}
response = client.post(
"/update/device-in-net/{}/".format(device_in_net.pk), data
)
response = client.post("/update/device-in-net/{}/".format(device_in_net.pk), data)
assert response.status_code == 302
device_in_net.refresh_from_db()
assert device_in_net.ip == data["ip"]

View File

@ -9,9 +9,7 @@ pytestmark = pytest.mark.django_db
def test_warranty_create_form(create_admin_user):
create_admin_user()
form = forms.WarrantyCreateForm(data={})
assert (
form.is_valid() is False
), "Should be false because no data was given"
assert form.is_valid() is False, "Should be false because no data was given"
device = mixer.blend("devices.Device")
@ -33,8 +31,7 @@ def test_warranty_create_form(create_admin_user):
form.is_valid() is False
), "Should be false because valid from is before valid until"
assert (
"Valid from date must be before valid until date"
== form.errors["__all__"][0]
"Valid from date must be before valid until date" == form.errors["__all__"][0]
)
@ -51,6 +48,5 @@ def test_warranty_update_form(create_admin_user):
form = forms.WarrantyUpdateForm(data=data)
assert form.is_valid() is False
assert (
"Valid from date must be before valid until date"
== form.errors["__all__"][0]
"Valid from date must be before valid until date" == form.errors["__all__"][0]
)

View File

@ -39,9 +39,7 @@ def test_warranties_view_plenty_of_time(create_admin_user):
user.save()
device = mixer.blend("devices.Device", customer=fixture["customer"])
more_than_one_year = datetime.date(datetime.today() + timedelta(400))
mixer.blend(
"devices.Warranty", device=device, valid_until=more_than_one_year
)
mixer.blend("devices.Warranty", device=device, valid_until=more_than_one_year)
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/warranties/")
@ -77,9 +75,7 @@ def test_warranties_view_warranty_one_year_till_expiration(create_admin_user):
user.save()
device = mixer.blend("devices.Device", customer=fixture["customer"])
not_one_year_more = datetime.date(datetime.today() + timedelta(200))
mixer.blend(
"devices.Warranty", device=device, valid_until=not_one_year_more
)
mixer.blend("devices.Warranty", device=device, valid_until=not_one_year_more)
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/warranties/")

View File

@ -4,9 +4,7 @@ from . import views
urlpatterns = [
path(
"customer/<int:pk>/devices/", views.devices_table_view, name="devices"
),
path("customer/<int:pk>/devices/", views.devices_table_view, name="devices"),
path("device/<int:pk>/", views.device_detail_view, name="device"),
path(
"manufacturer/<int:pk>/",

View File

@ -60,16 +60,12 @@ def device_detail_view(request, pk):
def devices_table_view(request, pk):
table = DevicesTable(Device.objects.filter(customer=pk))
RequestConfig(request).configure(table)
return render(
request, "devices/device_list.html", {"devices": table, "pk": pk}
)
return render(request, "devices/device_list.html", {"devices": table, "pk": pk})
@login_required
def warranties_view(request):
table = WarrantiesTable(
utils.objects_for_allowed_customers(Warranty, request.user)
)
table = WarrantiesTable(utils.objects_for_allowed_customers(Warranty, request.user))
RequestConfig(request).configure(table)
return render(request, "devices/warranties_list.html", {"devices": table})
@ -111,9 +107,7 @@ def device_update_view(request, pk):
"""
template_name = "devices/device_update.html"
request.session["device_to_update"] = pk
device = utils.get_object_with_view_permission(
Device, user=request.user, pk=pk
)
device = utils.get_object_with_view_permission(Device, user=request.user, pk=pk)
if request.method == "POST" and "save_device" in request.POST:
form = DeviceUpdateForm(request, request.POST, instance=device)
if form.is_valid():
@ -124,7 +118,7 @@ def device_update_view(request, pk):
return TemplateResponse(request, template_name, {"form": form})
class DeviceDeleteView(LoginRequiredMixin, DeleteView):
class DeviceDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Device
def get_success_url(self):
@ -160,7 +154,7 @@ class WarrantyUpdateView(LoginRequiredMixin, UpdateView):
return self.request.POST.get("previous_page")
class WarrantyDeleteView(LoginRequiredMixin, DeleteView):
class WarrantyDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Warranty
def get_success_url(self):
@ -195,7 +189,7 @@ class DeviceInNetUpdateView(LoginRequiredMixin, UpdateView):
return self.request.POST.get("previous_page")
class DeviceInNetDeleteView(LoginRequiredMixin, DeleteView):
class DeviceInNetDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = DeviceInNet
template_name = "devices/device_in_net_confirm_delete.html"

View File

@ -48,9 +48,7 @@ class LicenseWithUser(models.Model):
class Meta:
constraints = [
models.UniqueConstraint(
fields=["user", "license"], name="user per license"
)
models.UniqueConstraint(fields=["user", "license"], name="user per license")
]

View File

@ -61,9 +61,7 @@ def test_customer_license_table_no_license(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/customer/" + str(customer.id) + "/licenses/")
assert response.status_code == 200 and helper.not_in_content(
response, customer
)
assert response.status_code == 200 and helper.not_in_content(response, customer)
def test_customer_license_table_no_permission(create_admin_user):

View File

@ -41,7 +41,7 @@ def licenses_table_view(request, pk):
class LicenseWithComputerCreateView(LoginRequiredMixin, CreateView):
model = LicenseWithComputer
template_name = "licenses/license_with_computer_create.html"
fields = "__all__"
fields = "__all__" # type: ignore
def get_success_url(self):
return reverse("computer", args=(self.computer.pk,))
@ -57,7 +57,7 @@ class LicenseWithComputerCreateView(LoginRequiredMixin, CreateView):
}
class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView):
class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = LicenseWithComputer
template_name = "licenses/license_with_computer_confirm_delete.html"
@ -65,7 +65,7 @@ class LicenseWithComputerDeleteView(LoginRequiredMixin, DeleteView):
return reverse("computer", args=(self.object.computer.pk,))
class UserLicenseDeleteView(LoginRequiredMixin, DeleteView):
class UserLicenseDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = UserLicense
template_name = "licenses/license_confirm_delete.html"
@ -73,7 +73,7 @@ class UserLicenseDeleteView(LoginRequiredMixin, DeleteView):
return reverse("licenses", args=(self.object.customer.pk,))
class ComputerLicenseDeleteView(LoginRequiredMixin, DeleteView):
class ComputerLicenseDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = ComputerLicense
template_name = "licenses/license_confirm_delete.html"

View File

@ -3,9 +3,7 @@ import os
import sys
if __name__ == "__main__":
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "network_inventory.settings"
)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "network_inventory.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:

View File

@ -11,8 +11,6 @@ def net_view_permission(old_fuction):
if user.has_perm("customers.view_customer", net.customer):
return old_fuction(request, pk)
else:
return HttpResponseForbidden(
"You're not allowed to access this device."
)
return HttpResponseForbidden("You're not allowed to access this device.")
return new_function

View File

@ -15,9 +15,7 @@ def test_net_detail_view_no_permission(create_admin_user):
net = mixer.blend("nets.Net")
customer = mixer.blend("customers.Customer")
device = mixer.blend("computers.Computer", customer=customer)
mixer.blend(
"devices.DeviceInNet", device=device, net=net, ip="10.7.89.101"
)
mixer.blend("devices.DeviceInNet", device=device, net=net, ip="10.7.89.101")
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/net/" + str(net.id) + "/")
@ -28,9 +26,7 @@ def test_net_detail_view(create_admin_user):
fixture = create_admin_user()
net = mixer.blend("nets.Net", customer=mixer.SELECT)
device = mixer.blend("computers.Computer", customer=fixture["customer"])
device_in_net = DeviceInNet.objects.create(
device=device, net=net, ip="10.7.89.101"
)
device_in_net = DeviceInNet.objects.create(device=device, net=net, ip="10.7.89.101")
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/net/" + str(net.id) + "/")

View File

@ -29,12 +29,10 @@ def net_detail_view(request, pk):
net = get_object_or_404(Net, pk=pk)
table = NetDetailTable(DeviceInNet.objects.filter(net=net))
RequestConfig(request).configure(table)
return render(
request, "nets/net_details.html", {"table": table, "net": net}
)
return render(request, "nets/net_details.html", {"table": table, "net": net})
class NetDeleteView(LoginRequiredMixin, DeleteView):
class NetDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = Net
def get_success_url(self):

View File

@ -1,30 +0,0 @@
from socket import gethostname
from socket import gethostbyname_ex
from .base import *
ALLOWED_HOSTS = [
gethostname(),
] + list(set(gethostbyname_ex(gethostname())[2]))
CSRF_TRUSTED_ORIGINS = [
"http://localhost:8080",
]
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get("DJANGO_SECRET_KEY")
DEBUG = os.environ.get("DJANGO_DEBUG")
CRISPY_FAIL_SILENTLY = not DEBUG
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "postgres",
"USER": "postgres",
"HOST": "db",
"PORT": 5432,
"PASSWORD": "password",
}
}

View File

@ -2,12 +2,15 @@ from socket import gethostname
from socket import gethostbyname
from socket import getfqdn
import os
from .base import *
ALLOWED_HOSTS = [
"localhost",
"127.0.0.1",
"0.0.0.0",
getfqdn(),
gethostname(),
gethostbyname(gethostname()),
@ -26,10 +29,9 @@ CRISPY_FAIL_SILENTLY = not DEBUG
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "postgres",
"USER": "postgres",
"HOST": "localhost",
"PORT": 5432,
"PASSWORD": "password",
"NAME": "django",
"USER": os.environ.get("USER"),
"HOST": os.environ.get("PGHOST"),
"PORT": os.environ.get("PGPORT"),
}
}

View File

@ -11,8 +11,6 @@ def user_view_permission(old_fuction):
if user.has_perm("customers.view_customer", inventory_user.customer):
return old_fuction(request, pk)
else:
return HttpResponseForbidden(
"You're not allowed to access this device."
)
return HttpResponseForbidden("You're not allowed to access this device.")
return new_function

View File

@ -37,9 +37,7 @@ def test_customer_user_table_no_user(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/customer/" + str(customer.id) + "/users/")
assert response.status_code == 200 and helper.not_in_content(
response, customer
)
assert response.status_code == 200 and helper.not_in_content(response, customer)
def test_customer_user_table_no_permission(create_admin_user):

View File

@ -61,6 +61,4 @@ def test_group_detail_view_with_child_group(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/group/" + str(group.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, child_group
)
assert response.status_code == 200 and helper.in_content(response, child_group)

View File

@ -38,9 +38,7 @@ def test_user_detail_view_group(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/user/" + str(user.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, "Groups"
)
assert response.status_code == 200 and helper.in_content(response, "Groups")
def test_user_detail_view_mail_alias(create_admin_user):
@ -50,9 +48,7 @@ def test_user_detail_view_mail_alias(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/user/" + str(user.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, "Mail Alias"
)
assert response.status_code == 200 and helper.in_content(response, "Mail Alias")
def test_user_detail_view_license(create_admin_user):
@ -63,9 +59,7 @@ def test_user_detail_view_license(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/user/" + str(user.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, "License"
)
assert response.status_code == 200 and helper.in_content(response, "License")
def test_user_detail_view_computer(create_admin_user):
@ -75,9 +69,7 @@ def test_user_detail_view_computer(create_admin_user):
client = Client()
client.login(username="pharma-admin", password="password")
response = client.get("/user/" + str(user.id) + "/")
assert response.status_code == 200 and helper.in_content(
response, computer
)
assert response.status_code == 200 and helper.in_content(response, computer)
def test_user_detail_view_no_permission(create_admin_user):

View File

@ -52,7 +52,7 @@ def user_detail_view(request, pk):
)
class UserDeleteView(LoginRequiredMixin, DeleteView):
class UserDeleteView(LoginRequiredMixin, DeleteView): # type: ignore
model = User
def get_success_url(self):
@ -64,9 +64,7 @@ class UserDeleteView(LoginRequiredMixin, DeleteView):
def groups_table_view(request, pk):
customer = get_object_or_404(Customer, pk=pk)
groups_table = GroupsTable(
utils.get_objects_for_customer(
Group, user=request.user, customer_pk=pk
)
utils.get_objects_for_customer(Group, user=request.user, customer_pk=pk)
)
RequestConfig(request).configure(groups_table)
return TemplateResponse(
@ -81,9 +79,7 @@ def groups_table_view(request, pk):
@login_required
def group_detail_view(request, pk):
group = utils.get_object_with_view_permission(
Group, user=request.user, pk=pk
)
group = utils.get_object_with_view_permission(Group, user=request.user, pk=pk)
users = group.user_set.all()
groups = Group.objects.filter(parent_group=group)
print(groups)
@ -96,9 +92,7 @@ def group_detail_view(request, pk):
@login_required
def delete_group(request, pk):
group = utils.get_object_with_view_permission(
Group, user=request.user, pk=pk
)
group = utils.get_object_with_view_permission(Group, user=request.user, pk=pk)
if request.method == "POST":
group.delete()
return redirect("groups", pk=group.customer.pk)