Merge pull request #191532 from ambroisie/add-tandoor-recipes

This commit is contained in:
Sandro 2022-10-03 20:25:06 +02:00 committed by GitHub
commit 1385382014
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1212 additions and 2 deletions

View File

@ -194,6 +194,13 @@
<link linkend="opt-services.komga.enable">services.komga</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://tandoor.dev">Tandoor Recipes</link>,
a self-hosted multi-tenant recipe collection. Available as
<link xlink:href="options.html#opt-services.tandoor-recipes.enable">services.tandoor-recipes</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://hbase.apache.org/">HBase

View File

@ -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).

View File

@ -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

View File

@ -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";
};
};
};
}

View File

@ -612,6 +612,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 {};

View File

@ -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")
'';
})

View File

@ -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 ];
};
}

View File

@ -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!
'';
};
}

View File

@ -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";
};
}

View File

@ -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'

View File

@ -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

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 = [

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 = [

View File

@ -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 ];
};
}

View File

@ -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

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -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 ];
};
}

View File

@ -11566,6 +11566,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 { };

View File

@ -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 { };
@ -2499,10 +2501,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 { };
@ -2527,6 +2533,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 { };
@ -2555,6 +2563,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 { };
@ -2568,6 +2578,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 { };
@ -2602,6 +2614,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 { };
@ -2796,6 +2810,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 { };
@ -3126,6 +3142,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 { };
@ -4289,6 +4307,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 { };
@ -4862,6 +4882,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 { };
@ -5071,6 +5093,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 { };
@ -5700,8 +5724,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 { };
@ -7002,6 +7030,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 { };
@ -9532,6 +9562,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;
});