diff --git a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
index bfe04d89fa83b..cf5b5dd872c2f 100644
--- a/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
+++ b/nixos/doc/manual/from_md/release-notes/rl-2211.section.xml
@@ -194,6 +194,13 @@
services.komga.
+
+
+ Tandoor Recipes,
+ a self-hosted multi-tenant recipe collection. Available as
+ services.tandoor-recipes.
+
+
HBase
diff --git a/nixos/doc/manual/release-notes/rl-2211.section.md b/nixos/doc/manual/release-notes/rl-2211.section.md
index dcbe545a626cf..0d510b3a0df05 100644
--- a/nixos/doc/manual/release-notes/rl-2211.section.md
+++ b/nixos/doc/manual/release-notes/rl-2211.section.md
@@ -72,6 +72,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [Komga](https://komga.org/), a free and open source comics/mangas media server. Available as [services.komga](#opt-services.komga.enable).
+- [Tandoor Recipes](https://tandoor.dev), a self-hosted multi-tenant recipe collection. Available as [services.tandoor-recipes](options.html#opt-services.tandoor-recipes.enable).
+
- [HBase cluster](https://hbase.apache.org/), a distributed, scalable, big data store. Available as [services.hadoop.hbase](options.html#opt-services.hadoop.hbase.enable).
- [Sachet](https://github.com/messagebird/sachet/), an SMS alerting tool for the Prometheus Alertmanager. Available as [services.prometheus.sachet](#opt-services.prometheus.sachet.enable).
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index dec66e395aade..b53c2701892e4 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -653,6 +653,7 @@
./services/misc/svnserve.nix
./services/misc/synergy.nix
./services/misc/sysprof.nix
+ ./services/misc/tandoor-recipes.nix
./services/misc/taskserver
./services/misc/tiddlywiki.nix
./services/misc/tp-auto-kbbl.nix
diff --git a/nixos/modules/services/misc/tandoor-recipes.nix b/nixos/modules/services/misc/tandoor-recipes.nix
new file mode 100644
index 0000000000000..a349bcac93214
--- /dev/null
+++ b/nixos/modules/services/misc/tandoor-recipes.nix
@@ -0,0 +1,144 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+ cfg = config.services.tandoor-recipes;
+ pkg = cfg.package;
+
+ # SECRET_KEY through an env file
+ env = {
+ GUNICORN_CMD_ARGS = "--bind=${cfg.address}:${toString cfg.port}";
+ DEBUG = "0";
+ MEDIA_ROOT = "/var/lib/tandoor-recipes";
+ } // optionalAttrs (config.time.timeZone != null) {
+ TIMEZONE = config.time.timeZone;
+ } // (
+ lib.mapAttrs (_: toString) cfg.extraConfig
+ );
+
+ manage =
+ let
+ setupEnv = lib.concatStringsSep "\n" (mapAttrsToList (name: val: "export ${name}=\"${val}\"") env);
+ in
+ pkgs.writeShellScript "manage" ''
+ ${setupEnv}
+ exec ${pkg}/bin/tandoor-recipes "$@"
+ '';
+in
+{
+ meta.maintainers = with maintainers; [ ambroisie ];
+
+ options.services.tandoor-recipes = {
+ enable = mkOption {
+ type = lib.types.bool;
+ default = false;
+ description = lib.mdDoc ''
+ Enable Tandoor Recipes.
+
+ When started, the Tandoor Recipes database is automatically created if
+ it doesn't exist and updated if the package has changed. Both tasks are
+ achieved by running a Django migration.
+
+ A script to manage the instance (by wrapping Django's manage.py) is linked to
+ `/var/lib/tandoor-recipes/tandoor-recipes-manage`.
+ '';
+ };
+
+ address = mkOption {
+ type = types.str;
+ default = "localhost";
+ description = lib.mdDoc "Web interface address.";
+ };
+
+ port = mkOption {
+ type = types.port;
+ default = 8080;
+ description = lib.mdDoc "Web interface port.";
+ };
+
+ extraConfig = mkOption {
+ type = types.attrs;
+ default = { };
+ description = lib.mdDoc ''
+ Extra tandoor recipes config options.
+
+ See [the example dot-env file](https://raw.githubusercontent.com/vabene1111/recipes/master/.env.template)
+ for available options.
+ '';
+ example = {
+ ENABLE_SIGNUP = "1";
+ };
+ };
+
+ package = mkOption {
+ type = types.package;
+ default = pkgs.tandoor-recipes;
+ defaultText = literalExpression "pkgs.tandoor-recipes";
+ description = lib.mdDoc "The Tandoor Recipes package to use.";
+ };
+ };
+
+ config = mkIf cfg.enable {
+ systemd.services.tandoor-recipes = {
+ description = "Tandoor Recipes server";
+
+ serviceConfig = {
+ ExecStart = ''
+ ${pkg.python.pkgs.gunicorn}/bin/gunicorn recipes.wsgi
+ '';
+ Restart = "on-failure";
+
+ User = "tandoor_recipes";
+ DynamicUser = true;
+ StateDirectory = "tandoor-recipes";
+ WorkingDirectory = "/var/lib/tandoor-recipes";
+ RuntimeDirectory = "tandoor-recipes";
+
+ BindReadOnlyPaths = [
+ "${config.environment.etc."ssl/certs/ca-certificates.crt".source}:/etc/ssl/certs/ca-certificates.crt"
+ builtins.storeDir
+ "-/etc/resolv.conf"
+ "-/etc/nsswitch.conf"
+ "-/etc/hosts"
+ "-/etc/localtime"
+ "-/run/postgresql"
+ ];
+ CapabilityBoundingSet = "";
+ LockPersonality = true;
+ MemoryDenyWriteExecute = true;
+ PrivateDevices = true;
+ PrivateUsers = true;
+ ProtectClock = true;
+ ProtectControlGroups = true;
+ ProtectHome = true;
+ ProtectHostname = true;
+ ProtectKernelLogs = true;
+ ProtectKernelModules = true;
+ ProtectKernelTunables = true;
+ RestrictAddressFamilies = [ "AF_UNIX" "AF_INET" "AF_INET6" ];
+ RestrictNamespaces = true;
+ RestrictRealtime = true;
+ SystemCallArchitectures = "native";
+ # gunicorn needs setuid
+ SystemCallFilter = [ "@system-service" "~@privileged" "@resources" "@setuid" "@keyring" ];
+ UMask = "0066";
+ } // lib.optionalAttrs (cfg.port < 1024) {
+ AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
+ CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
+ };
+
+ wantedBy = [ "multi-user.target" ];
+
+ preStart = ''
+ ln -sf ${manage} tandoor-recipes-manage
+
+ # Let django migrate the DB as needed
+ ${pkg}/bin/tandoor-recipes migrate
+ '';
+
+ environment = env // {
+ PYTHONPATH = "${pkg.python.pkgs.makePythonPath pkg.propagatedBuildInputs}:${pkg}/lib/tandoor-recipes";
+ };
+ };
+ };
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 011d7b11b4f86..f95f9683c3d01 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -611,6 +611,7 @@ in {
systemd-shutdown = handleTest ./systemd-shutdown.nix {};
systemd-timesyncd = handleTest ./systemd-timesyncd.nix {};
systemd-misc = handleTest ./systemd-misc.nix {};
+ tandoor-recipes = handleTest ./tandoor-recipes.nix {};
taskserver = handleTest ./taskserver.nix {};
teeworlds = handleTest ./teeworlds.nix {};
telegraf = handleTest ./telegraf.nix {};
diff --git a/nixos/tests/tandoor-recipes.nix b/nixos/tests/tandoor-recipes.nix
new file mode 100644
index 0000000000000..54456238fe634
--- /dev/null
+++ b/nixos/tests/tandoor-recipes.nix
@@ -0,0 +1,43 @@
+import ./make-test-python.nix ({ lib, ... }: {
+ name = "tandoor-recipes";
+ meta.maintainers = with lib.maintainers; [ ambroisie ];
+
+ nodes.machine = { pkgs, ... }: {
+ # Setup using Postgres
+ services.tandoor-recipes = {
+ enable = true;
+
+ extraConfig = {
+ DB_ENGINE = "django.db.backends.postgresql";
+ POSTGRES_HOST = "/run/postgresql";
+ POSTGRES_USER = "tandoor_recipes";
+ POSTGRES_DB = "tandoor_recipes";
+ };
+ };
+
+ services.postgresql = {
+ enable = true;
+ ensureDatabases = [ "tandoor_recipes" ];
+ ensureUsers = [
+ {
+ name = "tandoor_recipes";
+ ensurePermissions."DATABASE tandoor_recipes" = "ALL PRIVILEGES";
+ }
+ ];
+ };
+
+ systemd.services = {
+ tandoor-recipes = {
+ after = [ "postgresql.service" ];
+ };
+ };
+ };
+
+ testScript = ''
+ machine.wait_for_unit("tandoor-recipes.service")
+
+ with subtest("Web interface gets ready"):
+ # Wait until server accepts connections
+ machine.wait_until_succeeds("curl -fs localhost:8080")
+ '';
+})
diff --git a/pkgs/applications/misc/tandoor-recipes/common.nix b/pkgs/applications/misc/tandoor-recipes/common.nix
new file mode 100644
index 0000000000000..77f1c3f9aef52
--- /dev/null
+++ b/pkgs/applications/misc/tandoor-recipes/common.nix
@@ -0,0 +1,19 @@
+{ lib, fetchFromGitHub }:
+rec {
+ version = "1.4.1";
+
+ src = fetchFromGitHub {
+ owner = "TandoorRecipes";
+ repo = "recipes";
+ rev = version;
+ sha256 = "sha256-Q/IwjSByCUXVYxhk3U7oWvlMxrJxyajhpsRyq67PVHY=";
+ };
+
+ yarnSha256 = "sha256-gH0q3pJ2BC5pAU9KSo3C9DDRUnpypoyLOEqKSrkxYrk=";
+
+ meta = with lib; {
+ homepage = "https://tandoor.dev/";
+ license = licenses.agpl3Only;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/applications/misc/tandoor-recipes/default.nix b/pkgs/applications/misc/tandoor-recipes/default.nix
new file mode 100644
index 0000000000000..008468f1b5e58
--- /dev/null
+++ b/pkgs/applications/misc/tandoor-recipes/default.nix
@@ -0,0 +1,140 @@
+{ callPackage
+, nixosTests
+, python3
+}:
+let
+ python = python3.override {
+ packageOverrides = self: super: {
+ django = super.django_4;
+
+ # Tests are incompatible with Django 4
+ django-js-reverse = super.django-js-reverse.overridePythonAttrs (_: {
+ doCheck = false;
+ });
+ };
+ };
+
+ common = callPackage ./common.nix { };
+
+ frontend = callPackage ./frontend.nix { };
+in
+python.pkgs.pythonPackages.buildPythonPackage rec {
+ pname = "tandoor-recipes";
+
+ inherit (common) version src;
+
+ format = "other";
+
+ patches = [
+ # Allow setting MEDIA_ROOT through environment variable
+ ./media-root.patch
+ ];
+
+ propagatedBuildInputs = with python.pkgs; [
+ beautifulsoup4
+ bleach
+ bleach-allowlist
+ boto3
+ cryptography
+ django
+ django-allauth
+ django-annoying
+ django-auth-ldap
+ django-autocomplete-light
+ django-cleanup
+ django-cors-headers
+ django-crispy-forms
+ django-hcaptcha
+ django-js-reverse
+ django-oauth-toolkit
+ django-prometheus
+ django-scopes
+ django-storages
+ django-tables2
+ django-webpack-loader
+ django_treebeard
+ djangorestframework
+ drf-writable-nested
+ gunicorn
+ icalendar
+ jinja2
+ lxml
+ markdown
+ microdata
+ pillow
+ psycopg2
+ pyppeteer
+ python-dotenv
+ pytube
+ pyyaml
+ recipe-scrapers
+ requests
+ six
+ uritemplate
+ validators
+ webdavclient3
+ whitenoise
+ ];
+
+ configurePhase = ''
+ runHook preConfigure
+
+ ln -sf ${frontend}/ cookbook/static/vue
+ cp ${frontend}/webpack-stats.json vue/
+
+ runHook postConfigure
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+
+ # Avoid dependency on django debug toolbar
+ export DEBUG=0
+
+ # See https://github.com/TandoorRecipes/recipes/issues/2043
+ mkdir cookbook/static/themes/maps/
+ touch cookbook/static/themes/maps/style.min.css.map
+ touch cookbook/static/themes/bootstrap.min.css.map
+ touch cookbook/static/css/bootstrap-vue.min.css.map
+
+ ${python.pythonForBuild.interpreter} manage.py collectstatic_js_reverse
+ ${python.pythonForBuild.interpreter} manage.py collectstatic
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out/lib
+ cp -r . $out/lib/tandoor-recipes
+ chmod +x $out/lib/tandoor-recipes/manage.py
+ makeWrapper $out/lib/tandoor-recipes/manage.py $out/bin/tandoor-recipes \
+ --prefix PYTHONPATH : "$PYTHONPATH"
+
+ runHook postInstall
+ '';
+
+ checkInputs = with python.pkgs; [
+ pytestCheckHook
+ pytest-django
+ pytest-factoryboy
+ ];
+
+ passthru = {
+ inherit frontend python;
+
+ updateScript = ./update.sh;
+
+ tests = {
+ inherit (nixosTests) tandoor-recipes;
+ };
+ };
+
+ meta = common.meta // {
+ description = ''
+ Application for managing recipes, planning meals, building shopping lists
+ and much much more!
+ '';
+ };
+}
diff --git a/pkgs/applications/misc/tandoor-recipes/frontend.nix b/pkgs/applications/misc/tandoor-recipes/frontend.nix
new file mode 100644
index 0000000000000..47dcde5aca517
--- /dev/null
+++ b/pkgs/applications/misc/tandoor-recipes/frontend.nix
@@ -0,0 +1,57 @@
+{ stdenv, fetchYarnDeps, fixup_yarn_lock, callPackage, nodejs-16_x }:
+let
+ common = callPackage ./common.nix { };
+in
+stdenv.mkDerivation {
+ pname = "tandoor-recipes-frontend";
+ inherit (common) version;
+
+ src = "${common.src}/vue";
+
+ yarnOfflineCache = fetchYarnDeps {
+ yarnLock = "${common.src}/vue/yarn.lock";
+ sha256 = common.yarnSha256;
+ };
+
+ nativeBuildInputs = [
+ fixup_yarn_lock
+ # Use Node JS 16 because of @achrinza/node-ipc@9.2.2
+ nodejs-16_x
+ nodejs-16_x.pkgs.yarn
+ ];
+
+ configurePhase = ''
+ runHook preConfigure
+
+ export HOME=$(mktemp -d)
+ yarn config --offline set yarn-offline-mirror "$yarnOfflineCache"
+ fixup_yarn_lock yarn.lock
+ command -v yarn
+ yarn install --frozen-lockfile --offline --no-progress --non-interactive
+ patchShebangs node_modules/
+
+ runHook postConfigure
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+
+ yarn --offline run build
+
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ cp -R ../cookbook/static/vue/ $out
+ cp webpack-stats.json $out
+ echo "${common.version}" > "$out/version"
+
+ runHook postInstall
+ '';
+
+ meta = common.meta // {
+ description = "Tandoor Recipes frontend";
+ };
+}
diff --git a/pkgs/applications/misc/tandoor-recipes/media-root.patch b/pkgs/applications/misc/tandoor-recipes/media-root.patch
new file mode 100644
index 0000000000000..8114ca8aaeb37
--- /dev/null
+++ b/pkgs/applications/misc/tandoor-recipes/media-root.patch
@@ -0,0 +1,17 @@
+diff --git a/recipes/settings.py b/recipes/settings.py
+index 5676fe0a..6c6f1747 100644
+--- a/recipes/settings.py
++++ b/recipes/settings.py
+@@ -426,10 +426,10 @@ if os.getenv('S3_ACCESS_KEY', ''):
+ AWS_S3_CUSTOM_DOMAIN = os.getenv('S3_CUSTOM_DOMAIN', '')
+
+ MEDIA_URL = os.getenv('MEDIA_URL', '/media/')
+- MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
++ MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(BASE_DIR, "mediafiles"))
+ else:
+ MEDIA_URL = os.getenv('MEDIA_URL', '/media/')
+- MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
++ MEDIA_ROOT = os.getenv('MEDIA_ROOT', os.path.join(BASE_DIR, "mediafiles"))
+
+ # Serve static files with gzip
+ STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
diff --git a/pkgs/applications/misc/tandoor-recipes/update.sh b/pkgs/applications/misc/tandoor-recipes/update.sh
new file mode 100755
index 0000000000000..49b6d5f98ae95
--- /dev/null
+++ b/pkgs/applications/misc/tandoor-recipes/update.sh
@@ -0,0 +1,42 @@
+#!/usr/bin/env nix-shell
+#!nix-shell -I nixpkgs=../../../../ -i bash -p nix wget prefetch-yarn-deps nix-prefetch-github jq
+
+# shellcheck shell=bash
+
+if [ -n "$GITHUB_TOKEN" ]; then
+ TOKEN_ARGS=(--header "Authorization: token $GITHUB_TOKEN")
+fi
+
+if [ "$#" -gt 1 ] || [[ "$1" == -* ]]; then
+ echo "Regenerates packaging data for the tandoor-recipes package."
+ echo "Usage: $0 [git release tag]"
+ exit 1
+fi
+
+version="$1"
+
+set -euo pipefail
+
+if [ -z "$version" ]; then
+ version="$(wget -O- "${TOKEN_ARGS[@]}" "https://api.github.com/repos/TandoorRecipes/recipes/releases?per_page=1" | jq -r '.[0].tag_name')"
+fi
+
+package_src="https://raw.githubusercontent.com/TandoorRecipes/recipes/$version"
+
+src_hash=$(nix-prefetch-github TandoorRecipes recipes --rev "${version}" | jq -r .sha256)
+
+tmpdir=$(mktemp -d)
+trap 'rm -rf "$tmpdir"' EXIT
+
+pushd "$tmpdir"
+wget "${TOKEN_ARGS[@]}" "$package_src/vue/yarn.lock"
+yarn_hash=$(prefetch-yarn-deps yarn.lock)
+popd
+
+# Use friendlier hashes
+src_hash=$(nix hash to-sri --type sha256 "$src_hash")
+yarn_hash=$(nix hash to-sri --type sha256 "$yarn_hash")
+
+sed -i -E -e "s#version = \".*\"#version = \"$version\"#" common.nix
+sed -i -E -e "s#sha256 = \".*\"#sha256 = \"$src_hash\"#" common.nix
+sed -i -E -e "s#yarnSha256 = \".*\"#yarnSha256 = \"$yarn_hash\"#" common.nix
diff --git a/pkgs/development/python-modules/bleach-allowlist/default.nix b/pkgs/development/python-modules/bleach-allowlist/default.nix
new file mode 100644
index 0000000000000..4c52df127b00d
--- /dev/null
+++ b/pkgs/development/python-modules/bleach-allowlist/default.nix
@@ -0,0 +1,31 @@
+{ lib
+, buildPythonPackage
+, fetchPypi
+, bleach
+}:
+
+buildPythonPackage rec {
+ pname = "bleach-allowlist";
+ version = "1.0.3";
+
+ src = fetchPypi {
+ inherit pname version;
+ sha256 = "sha256-VuIghgeaDmoxAK6Z5NuvIOslhUhlmOsOmUAIoRQo2ps=";
+ };
+
+ propagatedBuildInputs = [
+ bleach
+ ];
+
+ # No tests
+ doCheck = false;
+
+ pythonImportsCheck = [ "bleach_allowlist" ];
+
+ meta = with lib; {
+ description = "Curated lists of tags and attributes for sanitizing html";
+ homepage = "https://github.com/yourcelf/bleach-allowlist";
+ license = licenses.bsd2;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-annoying/default.nix b/pkgs/development/python-modules/django-annoying/default.nix
new file mode 100644
index 0000000000000..3e062f29a0e08
--- /dev/null
+++ b/pkgs/development/python-modules/django-annoying/default.nix
@@ -0,0 +1,48 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, fetchpatch
+, django
+, six
+, pytest-django
+, pytestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "django-annoying";
+ version = "0.10.6";
+
+ src = fetchFromGitHub {
+ owner = "skorokithakis";
+ repo = "django-annoying";
+ rev = "v${version}";
+ sha256 = "sha256-M1zOLr1Vjf2U0xlW66Mpno+S+b4IKLklN+kYxRaj6cA=";
+ };
+
+ patches = [
+ (fetchpatch {
+ name = "django-4-compatibility.patch";
+ url = "https://github.com/skorokithakis/django-annoying/pull/101/commits/51b5bd7bc8bb7a410400667e00d0813603df32bd.patch";
+ sha256 = "sha256-gLRlAtIHHJ85I88af3C3y+ZT+nXrj2KrV7QgOuEqspk=";
+ })
+ ];
+
+ propagatedBuildInputs = [
+ django
+ six
+ ];
+
+ DJANGO_SETTINGS_MODULE = "tests.settings";
+
+ checkInputs = [
+ pytest-django
+ pytestCheckHook
+ ];
+
+ meta = with lib; {
+ description = "A django application that tries to eliminate annoying things in the Django framework";
+ homepage = "https://skorokithakis.github.io/django-annoying/";
+ license = licenses.bsd3;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-autocomplete-light/default.nix b/pkgs/development/python-modules/django-autocomplete-light/default.nix
new file mode 100644
index 0000000000000..26138a4ab4af1
--- /dev/null
+++ b/pkgs/development/python-modules/django-autocomplete-light/default.nix
@@ -0,0 +1,73 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, django
+, six
+, pytestCheckHook
+, django-debug-toolbar
+, django-extensions
+, django-taggit
+, django_tagging
+, mock
+, pytest-django
+, selenium
+, splinter
+, sqlparse
+, tenacity
+, whitenoise
+}:
+
+buildPythonPackage rec {
+ pname = "django-autocomplete-light";
+ version = "3.9.4";
+
+ src = fetchFromGitHub {
+ owner = "yourlabs";
+ repo = "django-autocomplete-light";
+ rev = version;
+ sha256 = "sha256-YUiGN6q7ARM/rg7d+ykeDEYZDYjB+DHxMCmdme6QccU=";
+ };
+
+ propagatedBuildInputs = [
+ django
+ six
+ ];
+
+ # Too many un-packaged dependencies
+ doCheck = false;
+
+ checkInputs = [
+ pytestCheckHook
+ django-debug-toolbar
+ django-extensions
+ django-taggit
+ django_tagging
+ mock
+ pytest-django
+ selenium
+ splinter
+ sqlparse
+ tenacity
+ whitenoise
+
+ # FIXME: not packaged
+ # django-generic-m2m
+ # django-gm2m
+ # django-querysetsequence
+ # pytest-splinter
+ # dango-nested-admin
+ # djhacker
+ ];
+
+ # Taken from tox.ini
+ preCheck = "cd test_project";
+
+ pythonImportsCheck = [ "dal" ];
+
+ meta = with lib; {
+ description = "A fresh approach to autocomplete implementations, specially for Django";
+ homepage = "https://django-autocomplete-light.readthedocs.io";
+ license = licenses.bsd3;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-cors-headers/default.nix b/pkgs/development/python-modules/django-cors-headers/default.nix
index ace1dc4f2de50..452c00b6e37f8 100644
--- a/pkgs/development/python-modules/django-cors-headers/default.nix
+++ b/pkgs/development/python-modules/django-cors-headers/default.nix
@@ -8,13 +8,13 @@
buildPythonPackage rec {
pname = "django-cors-headers";
- version = "3.7.0";
+ version = "3.13.0";
src = fetchFromGitHub {
owner = "adamchainz";
repo = "django-cors-headers";
rev = version;
- sha256 = "1wc8cs1gpg9v98bq5qwnd4pcv043za50wd63gwkm86lbvjxyxynz";
+ sha256 = "sha256-pIyf4poW8/slxj4PVvmXpuYp//v5w00yU0Vz6Jiy2yM=";
};
propagatedBuildInputs = [
diff --git a/pkgs/development/python-modules/django-crispy-forms/default.nix b/pkgs/development/python-modules/django-crispy-forms/default.nix
new file mode 100644
index 0000000000000..2677d8ec5262c
--- /dev/null
+++ b/pkgs/development/python-modules/django-crispy-forms/default.nix
@@ -0,0 +1,45 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, django
+, pytestCheckHook
+, pytest-django
+}:
+
+buildPythonPackage rec {
+ pname = "django-crispy-forms";
+ version = "1.14.0";
+
+ src = fetchFromGitHub {
+ owner = "django-crispy-forms";
+ repo = "django-crispy-forms";
+ rev = version;
+ sha256 = "sha256-NZ2lWxsQHc7Qc4HDoWgjJTZ/bJHmjpBf3q1LVLtzA+8=";
+ };
+
+ propagatedBuildInputs = [
+ django
+ ];
+
+ # FIXME: RuntimeError: Model class source.crispy_forms.tests.forms.CrispyTestModel doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.
+ doCheck = false;
+
+ checkInputs = [
+ pytest-django
+ pytestCheckHook
+ ];
+
+ pytestFlagsArray = [
+ "--ds=crispy_forms.tests.test_settings"
+ "crispy_forms/tests/"
+ ];
+
+ pythonImportsCheck = [ "crispy_forms" ];
+
+ meta = with lib; {
+ description = "The best way to have DRY Django forms.";
+ homepage = "https://django-crispy-forms.readthedocs.io/en/latest/";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-hcaptcha/default.nix b/pkgs/development/python-modules/django-hcaptcha/default.nix
new file mode 100644
index 0000000000000..d200444ab50fb
--- /dev/null
+++ b/pkgs/development/python-modules/django-hcaptcha/default.nix
@@ -0,0 +1,32 @@
+{ lib
+, buildPythonPackage
+, fetchPypi
+, django
+}:
+
+buildPythonPackage rec {
+ pname = "django-hcaptcha";
+ version = "0.2.0";
+
+ src = fetchPypi {
+ inherit version;
+ pname = "django-hCaptcha";
+ sha256 = "sha256-slGerwzJeGWscvglMBEixc9h4eSFLWiVmUFgIirLbBo=";
+ };
+
+ propagatedBuildInputs = [
+ django
+ ];
+
+ # No tests
+ doCheck = false;
+
+ pythonImportsCheck = [ "hcaptcha" ];
+
+ meta = with lib; {
+ description = "Django hCaptcha provides a simple way to protect your django forms using hCaptcha";
+ homepage = "https://github.com/AndrejZbin/django-hcaptcha";
+ license = licenses.bsd3;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-js-reverse/default.nix b/pkgs/development/python-modules/django-js-reverse/default.nix
new file mode 100644
index 0000000000000..9757d9aa27825
--- /dev/null
+++ b/pkgs/development/python-modules/django-js-reverse/default.nix
@@ -0,0 +1,54 @@
+{ lib
+, buildPythonPackage
+, fetchpatch
+, fetchFromGitHub
+, python
+, django
+, nodejs
+, js2py
+, six
+}:
+
+buildPythonPackage rec {
+ pname = "django-js-reverse";
+ # Support for Django 4.0 not yet released
+ version = "unstable-2022-09-16";
+
+ src = fetchFromGitHub {
+ owner = "ierror";
+ repo = "django-js-reverse";
+ rev = "7cab78c4531780ab4b32033d5104ccd5be1a246a";
+ sha256 = "sha256-oA4R5MciDMcSsb+GAgWB5jhj+nl7E8t69u0qlx2G93E=";
+ };
+
+ patches = [
+ (fetchpatch {
+ name = "fix-requires_system_checks-list-or-tuple";
+ url = "https://github.com/ierror/django-js-reverse/commit/1477ba44b62c419d12ebec86e56973f1ae56f712.patch";
+ sha256 = "sha256-xUtCziewVhnCOaNWddJBH4/Vvhwjjq/wcQDvh2YzWMQ=";
+ })
+ ];
+
+ propagatedBuildInputs = [
+ django
+ ];
+
+ checkInputs = [
+ nodejs
+ js2py
+ six
+ ];
+
+ checkPhase = ''
+ ${python.interpreter} django_js_reverse/tests/unit_tests.py
+ '';
+
+ pythonImportsCheck = [ "django_js_reverse" ];
+
+ meta = with lib; {
+ description = "Javascript url handling for Django that doesn't hurt";
+ homepage = "https://django-js-reverse.readthedocs.io/en/latest/";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/django-oauth-toolkit/default.nix b/pkgs/development/python-modules/django-oauth-toolkit/default.nix
index 65bf5045a0351..7905c1c985fec 100644
--- a/pkgs/development/python-modules/django-oauth-toolkit/default.nix
+++ b/pkgs/development/python-modules/django-oauth-toolkit/default.nix
@@ -1,6 +1,7 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
+, pythonRelaxDepsHook
# propagates
, django
@@ -39,6 +40,11 @@ buildPythonPackage rec {
requests
];
+ nativeBuildInputs = [ pythonRelaxDepsHook ];
+ pythonRelaxDeps = [
+ "django"
+ ];
+
DJANGO_SETTINGS_MODULE = "tests.settings";
checkInputs = [
diff --git a/pkgs/development/python-modules/django-scopes/default.nix b/pkgs/development/python-modules/django-scopes/default.nix
new file mode 100644
index 0000000000000..26810445faad3
--- /dev/null
+++ b/pkgs/development/python-modules/django-scopes/default.nix
@@ -0,0 +1,38 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, django
+, pytestCheckHook
+, pytest-django
+}:
+
+buildPythonPackage rec {
+ pname = "django-scopes";
+ version = "1.2.0.post1";
+
+ src = fetchFromGitHub {
+ owner = "raphaelm";
+ repo = "django-scopes";
+ # No 1.2.0.post1 tag, see https://github.com/raphaelm/django-scopes/issues/27
+ rev = "0b93cdb6a8335cb02a8ea7296511358ba841d137";
+ sha256 = "sha256-djptJRkW1pfVbxhhs58fJA4d8dKZuvYRy01Aa3Btr+k=";
+ };
+
+ propagatedBuildInputs = [
+ django
+ ];
+
+ checkInputs = [
+ pytest-django
+ pytestCheckHook
+ ];
+
+ pythonImportsCheck = [ "django_scopes" ];
+
+ meta = with lib; {
+ description = "Safely separate multiple tenants in a Django database";
+ homepage = "https://github.com/raphaelm/django-scopes";
+ license = licenses.asl20;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/djangorestframework/default.nix b/pkgs/development/python-modules/djangorestframework/default.nix
index 7feb65620265f..ca81d7bd57bb3 100644
--- a/pkgs/development/python-modules/djangorestframework/default.nix
+++ b/pkgs/development/python-modules/djangorestframework/default.nix
@@ -1,6 +1,7 @@
{ lib
, buildPythonPackage
, fetchFromGitHub
+, fetchpatch
, coreapi
, django
, django-guardian
@@ -24,6 +25,15 @@ buildPythonPackage rec {
sha256 = "sha256-XmX6DZBZYzVCe72GERplAWt5jIjV/cYercZGb0pYjoc=";
};
+ patches = [
+ # See https://github.com/encode/django-rest-framework/issues/8608
+ # and https://github.com/encode/django-rest-framework/pull/8591/
+ (fetchpatch {
+ name = "fix-django-collect-static.patch";
+ url = "https://github.com/encode/django-rest-framework/pull/8591/commits/65943bb58deba6ee1a89fe4504f270ab1806fce6.patch";
+ sha256 = "sha256-wI7EzX9tlyyXAPrJEr+/2uTg7dVY98IKgh7Cc/NZo5k=";
+ })
+ ];
propagatedBuildInputs = [
django
diff --git a/pkgs/development/python-modules/drf-writable-nested/default.nix b/pkgs/development/python-modules/drf-writable-nested/default.nix
new file mode 100644
index 0000000000000..77ce7d37ff8d7
--- /dev/null
+++ b/pkgs/development/python-modules/drf-writable-nested/default.nix
@@ -0,0 +1,37 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, django
+, djangorestframework
+, pytestCheckHook
+, pytest-django
+}:
+
+buildPythonPackage rec {
+ pname = "drf-writable-nested";
+ version = "0.6.4";
+
+ src = fetchFromGitHub {
+ owner = "beda-software";
+ repo = "drf-writable-nested";
+ rev = "v${version}";
+ sha256 = "sha256-RybtXZ5HipQHaA2RV6TOKIpl6aI9V49mqXDhCH6lg58=";
+ };
+
+ propagatedBuildInputs = [
+ django
+ djangorestframework
+ ];
+
+ checkInputs = [
+ pytest-django
+ pytestCheckHook
+ ];
+
+ meta = with lib; {
+ description = "Writable nested model serializer for Django REST Framework";
+ homepage = "https://github.com/beda-software/drf-writable-nested";
+ license = licenses.bsd2;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/extruct/default.nix b/pkgs/development/python-modules/extruct/default.nix
new file mode 100644
index 0000000000000..39591ac55fa2e
--- /dev/null
+++ b/pkgs/development/python-modules/extruct/default.nix
@@ -0,0 +1,61 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, pythonRelaxDepsHook
+, html-text
+, jstyleson
+, lxml
+, mf2py
+, pyrdfa3
+, rdflib
+, six
+, w3lib
+, pytestCheckHook
+, mock
+}:
+
+buildPythonPackage rec {
+ pname = "extruct";
+ version = "0.13.0";
+
+ src = fetchFromGitHub {
+ owner = "scrapinghub";
+ repo = "extruct";
+ rev = "v${version}";
+ sha256 = "sha256-hf6b/tZLggHzgFmZ6aldZIBd17Ni7vCTIIzhNlyjvxw=";
+ };
+
+ nativeBuildInputs = [
+ pythonRelaxDepsHook
+ ];
+
+ # rdflib-jsonld functionality is part of rdblib from version 6 onwards
+ pythonRemoveDeps = [
+ "rdflib-jsonld"
+ ];
+
+ propagatedBuildInputs = [
+ html-text
+ jstyleson
+ lxml
+ mf2py
+ pyrdfa3
+ rdflib
+ six
+ w3lib
+ ];
+
+ checkInputs = [
+ mock
+ pytestCheckHook
+ ];
+
+ pythonImportsCheck = [ "extruct" ];
+
+ meta = with lib; {
+ description = "Extract embedded metadata from HTML markup";
+ homepage = "https://github.com/scrapinghub/extruct";
+ license = licenses.bsd3;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/html-text/default.nix b/pkgs/development/python-modules/html-text/default.nix
new file mode 100644
index 0000000000000..997cf053a5f78
--- /dev/null
+++ b/pkgs/development/python-modules/html-text/default.nix
@@ -0,0 +1,37 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, lxml
+, six
+, pytestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "html-text";
+ version = "0.5.2";
+
+ src = fetchFromGitHub {
+ owner = "TeamHG-Memex";
+ repo = "html-text";
+ rev = version;
+ sha256 = "sha256-jw/hpz0QfcgP5OEJcmre0h1OzOfpPtaROxHm+YUqces=";
+ };
+
+ propagatedBuildInputs = [
+ lxml
+ ];
+
+ checkInputs = [
+ pytestCheckHook
+ six
+ ];
+
+ pythonImportsCheck = [ "html_text" ];
+
+ meta = with lib; {
+ description = "Extract text from HTML";
+ homepage = "https://github.com/TeamHG-Memex/html-text";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/jstyleson/default.nix b/pkgs/development/python-modules/jstyleson/default.nix
new file mode 100644
index 0000000000000..be6eff6755f56
--- /dev/null
+++ b/pkgs/development/python-modules/jstyleson/default.nix
@@ -0,0 +1,31 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, pytestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "jstyleson";
+ version = "0.0.2";
+
+ src = fetchFromGitHub {
+ owner = "linjackson78";
+ repo = "jstyleson";
+ # https://github.com/linjackson78/jstyleson/issues/6
+ rev = "544b9fdb43339cdd15dd03dc69a6d0f36dd73241";
+ sha256 = "sha256-s/0DDfy+07TuUNjHPqKRT3xMMQl6spZCacB7Dweof7A=";
+ };
+
+ checkInputs = [
+ pytestCheckHook
+ ];
+
+ pythonImportsCheck = [ "jstyleson" ];
+
+ meta = with lib; {
+ description = "A python library to parse JSON with js-style comments";
+ homepage = "https://github.com/linjackson78/jstyleson";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/language-tags/default.nix b/pkgs/development/python-modules/language-tags/default.nix
new file mode 100644
index 0000000000000..ea3be88335c90
--- /dev/null
+++ b/pkgs/development/python-modules/language-tags/default.nix
@@ -0,0 +1,30 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, pytestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "language-tags";
+ version = "1.1.0";
+
+ src = fetchFromGitHub {
+ owner = "OnroerendErfgoed";
+ repo = "language-tags";
+ rev = version;
+ sha256 = "sha256-4Ira3EMS64AM8I3SLmUm+m6V5vwtDYf8WDmVDvI+ZOw=";
+ };
+
+ checkInputs = [
+ pytestCheckHook
+ ];
+
+ pythonImportsCheck = [ "language_tags" ];
+
+ meta = with lib; {
+ description = "Dealing with IANA language tags in Python";
+ homepage = "https://language-tags.readthedocs.io/en/latest/";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/mf2py/default.nix b/pkgs/development/python-modules/mf2py/default.nix
new file mode 100644
index 0000000000000..110da8e60f8f6
--- /dev/null
+++ b/pkgs/development/python-modules/mf2py/default.nix
@@ -0,0 +1,43 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, beautifulsoup4
+, html5lib
+, requests
+, lxml
+, mock
+, nose
+}:
+
+buildPythonPackage rec {
+ pname = "mf2py";
+ version = "1.1.2";
+
+ src = fetchFromGitHub {
+ owner = "microformats";
+ repo = "mf2py";
+ rev = version;
+ sha256 = "sha256-9pAD/eCmc/l7LGmKixDhZy3hhj1jCmcyo9wbqgtz/wI=";
+ };
+
+ propagatedBuildInputs = [
+ beautifulsoup4
+ html5lib
+ requests
+ ];
+
+ checkInputs = [
+ lxml
+ mock
+ nose
+ ];
+
+ pythonImportsCheck = [ "mf2py" ];
+
+ meta = with lib; {
+ description = "Microformats2 parser written in Python";
+ homepage = "https://microformats.org/wiki/mf2py";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/microdata/default.nix b/pkgs/development/python-modules/microdata/default.nix
new file mode 100644
index 0000000000000..39ed19395c5df
--- /dev/null
+++ b/pkgs/development/python-modules/microdata/default.nix
@@ -0,0 +1,35 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, html5lib
+, unittestCheckHook
+}:
+
+buildPythonPackage rec {
+ pname = "microdata";
+ version = "0.8.0";
+
+ src = fetchFromGitHub {
+ owner = "edsu";
+ repo = "microdata";
+ rev = "v${version}";
+ sha256 = "sha256-BAygCLBLxZ033ZWRFSR52dSM2nPY8jXplDXQ8WW3KPo=";
+ };
+
+ propagatedBuildInputs = [
+ html5lib
+ ];
+
+ checkInputs = [
+ unittestCheckHook
+ ];
+
+ pythonImportsCheck = [ "microdata" ];
+
+ meta = with lib; {
+ description = "Library for extracting html microdata";
+ homepage = "https://github.com/edsu/microdata";
+ license = licenses.cc0;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/pyrdfa3/default.nix b/pkgs/development/python-modules/pyrdfa3/default.nix
new file mode 100644
index 0000000000000..97768e7fed507
--- /dev/null
+++ b/pkgs/development/python-modules/pyrdfa3/default.nix
@@ -0,0 +1,42 @@
+{ lib
+, buildPythonPackage
+, fetchPypi
+, isPy27
+, rdflib
+, html5lib
+}:
+
+buildPythonPackage rec {
+ pname = "pyrdfa3";
+ version = "3.5.3";
+ disabled = isPy27;
+
+ src = fetchPypi {
+ inherit version;
+ pname = "pyRdfa3";
+ sha256 = "sha256-FXZjqSuH3zRbb2m94jXf9feXiRYI4S/h5PqNrWhxMa4=";
+ };
+
+ postPatch = ''
+ substituteInPlace setup.py \
+ --replace "'html = pyRdfa.rdflibparsers:StructuredDataParser'" "'html = pyRdfa.rdflibparsers:StructuredDataParser'," \
+ --replace "'hturtle = pyRdfa.rdflibparsers:HTurtleParser'" "'hturtle = pyRdfa.rdflibparsers:HTurtleParser',"
+ '';
+
+ propagatedBuildInputs = [
+ rdflib
+ html5lib
+ ];
+
+ # Does not work with python3
+ doCheck = false;
+
+ pythonImportsCheck = [ "pyRdfa" ];
+
+ meta = with lib; {
+ description = "RDFa 1.1 distiller/parser library";
+ homepage = "https://www.w3.org/2012/pyRdfa/";
+ license = licenses.w3c;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/development/python-modules/recipe-scrapers/default.nix b/pkgs/development/python-modules/recipe-scrapers/default.nix
new file mode 100644
index 0000000000000..897a8239f0fa6
--- /dev/null
+++ b/pkgs/development/python-modules/recipe-scrapers/default.nix
@@ -0,0 +1,50 @@
+{ lib
+, buildPythonPackage
+, fetchFromGitHub
+, beautifulsoup4
+, extruct
+, language-tags
+, regex
+, requests
+, pytestCheckHook
+, responses
+}:
+
+buildPythonPackage rec {
+ pname = "recipe-scrapers";
+ version = "14.14.0";
+
+ src = fetchFromGitHub {
+ owner = "hhursev";
+ repo = "recipe-scrapers";
+ rev = version;
+ sha256 = "sha256-3qrjNd1jX4JP3qG9YX8MQqwPh8cvfkZa1tEk0uCwego=";
+ };
+
+ propagatedBuildInputs = [
+ beautifulsoup4
+ extruct
+ language-tags
+ regex
+ requests
+ ];
+
+ checkInputs = [
+ pytestCheckHook
+ responses
+ ];
+
+ disabledTestPaths = [
+ # This is not actual code, just some pre-written boiler-plate template
+ "templates/test_scraper.py"
+ ];
+
+ pythonImportsCheck = [ "recipe_scrapers" ];
+
+ meta = with lib; {
+ description = "Python package for scraping recipes data ";
+ homepage = "https://github.com/hhursev/recipe-scrapers";
+ license = licenses.mit;
+ maintainers = with maintainers; [ ambroisie ];
+ };
+}
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index eecc50b96c104..6953dfcc453d8 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -11545,6 +11545,8 @@ with pkgs;
inherit (callPackages ../applications/networking/taler { })
taler-exchange taler-merchant;
+ tandoor-recipes = callPackage ../applications/misc/tandoor-recipes { };
+
tangram = callPackage ../applications/networking/instant-messengers/tangram { };
t1utils = callPackage ../tools/misc/t1utils { };
diff --git a/pkgs/top-level/python-packages.nix b/pkgs/top-level/python-packages.nix
index e68e868229164..de5e9c571afd4 100644
--- a/pkgs/top-level/python-packages.nix
+++ b/pkgs/top-level/python-packages.nix
@@ -1385,6 +1385,8 @@ in {
bleach = callPackage ../development/python-modules/bleach { };
+ bleach-allowlist = callPackage ../development/python-modules/bleach-allowlist { };
+
bleak = callPackage ../development/python-modules/bleak { };
bleak-retry-connector = callPackage ../development/python-modules/bleak-retry-connector { };
@@ -2497,10 +2499,14 @@ in {
django-anymail = callPackage ../development/python-modules/django-anymail { };
+ django-annoying = callPackage ../development/python-modules/django-annoying { };
+
django-appconf = callPackage ../development/python-modules/django-appconf { };
django-auth-ldap = callPackage ../development/python-modules/django-auth-ldap { };
+ django-autocomplete-light = callPackage ../development/python-modules/django-autocomplete-light { };
+
django-cache-url = callPackage ../development/python-modules/django-cache-url { };
django-cacheops = callPackage ../development/python-modules/django-cacheops { };
@@ -2525,6 +2531,8 @@ in {
django-cors-headers = callPackage ../development/python-modules/django-cors-headers { };
+ django-crispy-forms = callPackage ../development/python-modules/django-crispy-forms { };
+
django-cryptography = callPackage ../development/python-modules/django-cryptography { };
django-csp = callPackage ../development/python-modules/django-csp { };
@@ -2553,6 +2561,8 @@ in {
django-haystack = callPackage ../development/python-modules/django-haystack { };
+ django-hcaptcha = callPackage ../development/python-modules/django-hcaptcha { };
+
django-health-check = callPackage ../development/python-modules/django-health-check { };
django_hijack_admin = callPackage ../development/python-modules/django-hijack-admin { };
@@ -2566,6 +2576,8 @@ in {
django-js-asset = callPackage ../development/python-modules/django-js-asset { };
+ django-js-reverse = callPackage ../development/python-modules/django-js-reverse { };
+
django-logentry-admin = callPackage ../development/python-modules/django-logentry-admin { };
django-mailman3 = callPackage ../development/python-modules/django-mailman3 { };
@@ -2600,6 +2612,8 @@ in {
django-q = callPackage ../development/python-modules/django-q { };
+ django-scopes = callPackage ../development/python-modules/django-scopes { };
+
djangoql = callPackage ../development/python-modules/djangoql { };
django-ranged-response = callPackage ../development/python-modules/django-ranged-response { };
@@ -2794,6 +2808,8 @@ in {
drf-spectacular-sidecar = callPackage ../development/python-modules/drf-spectacular-sidecar { };
+ drf-writable-nested = callPackage ../development/python-modules/drf-writable-nested { };
+
drf-yasg = callPackage ../development/python-modules/drf-yasg { };
drivelib = callPackage ../development/python-modules/drivelib { };
@@ -3124,6 +3140,8 @@ in {
extras = callPackage ../development/python-modules/extras { };
+ extruct = callPackage ../development/python-modules/extruct { };
+
eyeD3 = callPackage ../development/python-modules/eyed3 { };
ezdxf = callPackage ../development/python-modules/ezdxf { };
@@ -4287,6 +4305,8 @@ in {
html-sanitizer = callPackage ../development/python-modules/html-sanitizer { };
+ html-text = callPackage ../development/python-modules/html-text { };
+
HTSeq = callPackage ../development/python-modules/HTSeq { };
httmock = callPackage ../development/python-modules/httmock { };
@@ -4858,6 +4878,8 @@ in {
json-tricks = callPackage ../development/python-modules/json-tricks { };
+ jstyleson = callPackage ../development/python-modules/jstyleson { };
+
jug = callPackage ../development/python-modules/jug { };
junitparser = callPackage ../development/python-modules/junitparser { };
@@ -5065,6 +5087,8 @@ in {
language-data = callPackage ../development/python-modules/language-data { };
+ language-tags = callPackage ../development/python-modules/language-tags { };
+
lark = callPackage ../development/python-modules/lark { };
latexcodec = callPackage ../development/python-modules/latexcodec { };
@@ -5694,8 +5718,12 @@ in {
mezzanine = callPackage ../development/python-modules/mezzanine { };
+ mf2py = callPackage ../development/python-modules/mf2py { };
+
micawber = callPackage ../development/python-modules/micawber { };
+ microdata = callPackage ../development/python-modules/microdata { };
+
midiutil = callPackage ../development/python-modules/midiutil { };
mido = callPackage ../development/python-modules/mido { };
@@ -6995,6 +7023,8 @@ in {
pypoolstation = callPackage ../development/python-modules/pypoolstation { };
+ pyrdfa3 = callPackage ../development/python-modules/pyrdfa3 { };
+
pyrevolve = callPackage ../development/python-modules/pyrevolve { };
pyrfxtrx = callPackage ../development/python-modules/pyrfxtrx { };
@@ -9525,6 +9555,8 @@ in {
recaptcha_client = callPackage ../development/python-modules/recaptcha_client { };
+ recipe-scrapers = callPackage ../development/python-modules/recipe-scrapers { };
+
recoll = toPythonModule (pkgs.recoll.override {
python3Packages = self;
});