diff --git a/all-packages.nix b/all-packages.nix index 9e467c1a..bfc60df1 100644 --- a/all-packages.nix +++ b/all-packages.nix @@ -9,6 +9,20 @@ liberaforms-env = callPackage ./pkgs/liberaforms/env.nix {}; libgnunetchat = callPackage ./pkgs/libgnunetchat {}; librecast = callPackage ./pkgs/librecast {inherit lcrq;}; + pretalx-mysql = callPackage ./pkgs/pretalx { + withMysql = true; + withRedis = true; + }; + pretalx-postgresql = callPackage ./pkgs/pretalx { + withPostgresql = true; + withRedis = true; + }; + pretalx = callPackage ./pkgs/pretalx { + withMysql = true; + withPostgresql = true; + withRedis = true; + withTest = true; + }; }; nixpkgs-candidates = { diff --git a/configs/all-configurations.nix b/configs/all-configurations.nix index 56fa6114..dd9f4d30 100644 --- a/configs/all-configurations.nix +++ b/configs/all-configurations.nix @@ -1,3 +1,15 @@ -{pkgs}: { +{ liberaforms-container = import ./liberaforms/container.nix; + pretalx-postgresql = { + imports = [ + ./pretalx/pretalx.nix + ./pretalx/postgresql.nix + ]; + }; + pretalx-mysql = { + imports = [ + ./pretalx/pretalx.nix + ./pretalx/mysql.nix + ]; + }; } diff --git a/configs/liberaforms/container.nix b/configs/liberaforms/container.nix index a737ec89..4f02b998 100644 --- a/configs/liberaforms/container.nix +++ b/configs/liberaforms/container.nix @@ -1,10 +1,4 @@ -{ - pkgs, - lib, - ... -}: { - imports = [../../modules/liberaforms.nix]; - +{...}: { boot.isContainer = true; networking.useDHCP = false; @@ -26,4 +20,6 @@ }; system.stateVersion = "22.11"; + + nixpkgs.hostPlatform = "x86_64-linux"; } diff --git a/configs/pretalx/mysql.nix b/configs/pretalx/mysql.nix new file mode 100644 index 00000000..76c8fee7 --- /dev/null +++ b/configs/pretalx/mysql.nix @@ -0,0 +1,24 @@ +{ + config, + pkgs, + ... +}: { + services = { + pretalx.database = { + backend = "mysql"; + host = "/var/run/mysqld/mysqld.sock"; + }; + + mysql = { + enable = true; + package = pkgs.mysql; + ensureUsers = [ + { + name = config.services.pretalx.database.user; + ensurePermissions."${config.services.pretalx.database.name}.*" = "ALL PRIVILEGES"; + } + ]; + ensureDatabases = [config.services.pretalx.database.name]; + }; + }; +} diff --git a/configs/pretalx/postgresql.nix b/configs/pretalx/postgresql.nix new file mode 100644 index 00000000..f7c14524 --- /dev/null +++ b/configs/pretalx/postgresql.nix @@ -0,0 +1,17 @@ +{config, ...}: { + services = { + pretalx.database.backend = "postgresql"; + + postgresql = { + enable = true; + authentication = "local all all trust"; + ensureUsers = [ + { + name = config.services.pretalx.database.user; + ensurePermissions."DATABASE \"${config.services.pretalx.database.name}\"" = "ALL PRIVILEGES"; + } + ]; + ensureDatabases = [config.services.pretalx.database.name]; + }; + }; +} diff --git a/configs/pretalx/pretalx.nix b/configs/pretalx/pretalx.nix new file mode 100644 index 00000000..2d50dbdd --- /dev/null +++ b/configs/pretalx/pretalx.nix @@ -0,0 +1,84 @@ +{ + config, + pkgs, + ... +}: { + imports = [ + ./vm.nix + ]; + + nixpkgs.hostPlatform = "x86_64-linux"; + + networking = { + firewall.allowedTCPPorts = [config.services.nginx.defaultHTTPListenPort]; + hostName = "server"; + domain = "example.com"; + }; + + sops = { + secrets = let + pretalxSecret = { + # For a production configuration also `sopsFile` is required. + # See . + owner = config.services.pretalx.user; + group = config.services.pretalx.group; + }; + in { + "pretalx/database/password" = pretalxSecret; + "pretalx/redis/location" = pretalxSecret; + "pretalx/init/admin/password" = pretalxSecret; + "pretalx/celery/backend" = pretalxSecret; + "pretalx/celery/broker" = pretalxSecret; + }; + }; + + services = { + pretalx = { + enable = true; + package = pkgs.pretalx; + nginx = { + # For a production configuration use this attribute set to configure the virtual host for pretalx. + }; + database = { + user = "pretalx"; + passwordFile = config.sops.secrets."pretalx/database/password".path; + }; + redis = { + enable = true; + locationFile = config.sops.secrets."pretalx/redis/location".path; + }; + celery = { + enable = true; + backendFile = config.sops.secrets."pretalx/celery/backend".path; + brokerFile = config.sops.secrets."pretalx/celery/broker".path; + }; + + init = { + admin = { + email = "pretalx@localhost"; + passwordFile = config.sops.secrets."pretalx/init/admin/password".path; + }; + organiser = { + name = "NGI Packages"; + slug = "ngipkgs"; + }; + }; + mail.enable = false; + }; + + redis.servers."pretalx" = { + enable = true; + user = config.services.pretalx.user; + }; + + nginx = { + enable = true; + recommendedTlsSettings = true; + recommendedOptimisation = true; + recommendedGzipSettings = true; + recommendedProxySettings = true; + }; + }; + + system.stateVersion = "22.11"; +} diff --git a/configs/pretalx/vm.nix b/configs/pretalx/vm.nix new file mode 100644 index 00000000..dbbfc7d8 --- /dev/null +++ b/configs/pretalx/vm.nix @@ -0,0 +1,10 @@ +{modulesPath, ...}: { + imports = [ + "${modulesPath}/virtualisation/qemu-vm.nix" + ]; + + sops = { + age.keyFile = ./postgresql.nix; + defaultSopsFile = ./postgresql.nix; + }; +} diff --git a/flake.lock b/flake.lock index d9e7201e..230fa948 100644 --- a/flake.lock +++ b/flake.lock @@ -89,15 +89,53 @@ "type": "github" } }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1691874659, + "narHash": "sha256-qgmixg0c/CRNT2p9Ad35kaC7NzYVZ6GRooErYI7OGJM=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "efeed708ece1a9f4ae0506ae4a4d7da264a74102", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.05", + "repo": "nixpkgs", + "type": "github" + } + }, "root": { "inputs": { "flake-utils": "flake-utils", "nix-php-composer-builder": "nix-php-composer-builder", "nixpkgs": "nixpkgs", + "sops-nix": "sops-nix", "systems": "systems", "treefmt-nix": "treefmt-nix" } }, + "sops-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1692127428, + "narHash": "sha256-+e9dD67mpGLBhhqdv7A7i1g/r2AT/PmqthWaYHyVZR4=", + "owner": "Mic92", + "repo": "sops-nix", + "rev": "f81e73cf9a4ef4b949b9225be3daa1e586c096da", + "type": "github" + }, + "original": { + "owner": "Mic92", + "repo": "sops-nix", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1680978846, diff --git a/flake.nix b/flake.nix index 3fa47f7e..4465abb1 100644 --- a/flake.nix +++ b/flake.nix @@ -4,11 +4,15 @@ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; inputs.nix-php-composer-builder.url = "github:loophp/nix-php-composer-builder"; inputs.flake-utils.url = "github:numtide/flake-utils"; - # Set the defaultSystem list for flake-utils to only x86_64-linux + # Set default system to `x86_64-linux`, + # as we currently only support Linux. + # See for plans to support Darwin. inputs.systems.url = "github:nix-systems/x86_64-linux"; inputs.flake-utils.inputs.systems.follows = "systems"; inputs.treefmt-nix.url = "github:numtide/treefmt-nix"; inputs.treefmt-nix.inputs.nixpkgs.follows = "nixpkgs"; + inputs.sops-nix.url = "github:Mic92/sops-nix"; + inputs.sops-nix.inputs.nixpkgs.follows = "nixpkgs"; outputs = { self, @@ -16,60 +20,101 @@ nix-php-composer-builder, flake-utils, treefmt-nix, + sops-nix, ... - }: let - buildOutputs = system: let - pkgs = import nixpkgs { - inherit system; - overlays = [ - nix-php-composer-builder.overlays.default - ]; - }; - treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; - in { - packages = import ./all-packages.nix { - inherit (pkgs) newScope; - }; - nixosModules = { - modules = import ./modules/all-modules.nix; - ngipkgs = {...}: { - # Inject an additional argument into the module system evaluation. - # This way our package set can be accessed separately and we don't have - # to muck around with overlays (which don't work with flakes as you'd expect) - _module.args.ngipkgs = self.packages.${system}; + }: + with builtins; let + importPackages = pkgs: + import ./all-packages.nix { + inherit (pkgs) newScope; }; - }; - formatter = treefmtEval.config.build.wrapper; - }; + importNixpkgs = system: overlays: + import nixpkgs { + inherit system overlays; + }; - checkOutputs = system: let - pkgs = nixpkgs.legacyPackages.${system}; - treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; - in { - # Configurations have to go in checkOutputs (ie, avoid `eachDefaultSystem`) to generate - # a single attribute name for nixos-container deployments (``), because - # nixos-container can't parse dot-separated sequence attribute paths (`x86_64-linux.`). - nixosConfigurations = let - all-configurations = import ./configs/all-configurations.nix {inherit pkgs;}; - inject-ngipkgs = k: v: pkgs.nixos ({...}: {imports = [self.nixosModules.${system}.ngipkgs v];}); - in - builtins.mapAttrs inject-ngipkgs all-configurations; + importNixosConfigurations = import ./configs/all-configurations.nix; - # To generate a Hydra jobset for CI builds of all packages - # https://hydra.ngi0.nixos.org/jobset/ngipkgs/main - hydraJobs = { - packages.${system} = self.packages.${system}; - }; + loadTreefmt = pkgs: treefmt-nix.lib.evalModule pkgs ./treefmt.nix; - # For .github/workflows/ci.yaml to *build* all packages, because - # `nix flake check` only evaluates packages, but it builds checks. - checks.${system} = - self.packages.${system} + # Attribute set containing all modules obtained via `inputs` and defined in this flake towards definition of `nixosConfigurations` and `nixosTests`. + extendedModules = + self.nixosModules // { - formatting = treefmtEval.config.build.check self; + sops-nix = sops-nix.nixosModules.default; }; - }; - in - (flake-utils.lib.eachDefaultSystem buildOutputs) // (checkOutputs "x86_64-linux"); + + # Compute outputs that are invariant in the system architecture. + allSystemsOutputs = system: let + pkgs = importNixpkgs system [ + nix-php-composer-builder.overlays.default + ]; + treefmtEval = loadTreefmt pkgs; + in { + packages = importPackages pkgs; + formatter = treefmtEval.config.build.wrapper; + }; + in + # We merge three attribute sets to construct all outputs: + # 1. Outputs that are invariant in the system architecture + # via `flake-utils.lib.eachDefaultSystem`. + # 2. Outputs that are specific to a system architecture + # (as of 2023-08-22, only `x86_64-linux`). + # 3. Outputs that are not tied to any system at all. + # + # 1. + (flake-utils.lib.eachDefaultSystem allSystemsOutputs) + # + # 2. + // (let + linuxSystem = "x86_64-linux"; + pkgs = importNixpkgs linuxSystem [self.overlays.default]; + treefmtEval = loadTreefmt pkgs; + in { + # Github Actions executes `nix flake check` therefore this output + # should only contain derivations that can built within CI. + # See `.github/workflows/ci.yaml`. + checks.${linuxSystem} = + # For `nix flake check` to *build* all packages, because by default + # `nix flake check` only evaluates packages and does not build them. + self.packages.${linuxSystem} + // { + formatting = treefmtEval.config.build.check self; + }; + + # To generate a Hydra jobset for CI builds of all packages. + # See . + hydraJobs.packages.${linuxSystem} = self.packages.${linuxSystem}; + + # `nixosTests` is a non-standard name for a flake output. + # See . + nixosTests.${linuxSystem} = mapAttrs (_: pkgs.nixosTest) (import ./tests/all-tests.nix { + modules = extendedModules; + configurations = importNixosConfigurations; + }); + }) + # + # 3. + // { + nixosConfigurations = + mapAttrs ( + _: config: + nixpkgs.lib.nixosSystem { + modules = [config] ++ nixpkgs.lib.attrValues extendedModules; + } + ) + importNixosConfigurations; + + nixosModules = + (import ./modules/all-modules.nix) + // { + # The default module adds the default overlay on top of nixpkgs. + # This is so that `ngipkgs` can be used alongside `nixpkgs` in a configuration. + default.nixpkgs.overlays = [self.overlays.default]; + }; + + # Overlays a package set (e.g. nixpkgs) with the packages defined in this flake. + overlays.default = final: prev: importPackages prev; + }; } diff --git a/maintainers/maintainers-list.nix b/maintainers/maintainers-list.nix new file mode 100644 index 00000000..2fad6eff --- /dev/null +++ b/maintainers/maintainers-list.nix @@ -0,0 +1,14 @@ +{ + augustebaum = { + email = "auguste.apple@gmail.com"; + github = "augustebaum"; + githubId = 52001167; + name = "Auguste Baum"; + }; + kubaneko = { + email = "kubanek0ondrej@gmail.com"; + github = "kubaneko"; + githubId = 71923533; + name = "Ondřej Kubánek"; + }; +} diff --git a/modules/all-modules.nix b/modules/all-modules.nix index 0e5b1adf..663ac964 100644 --- a/modules/all-modules.nix +++ b/modules/all-modules.nix @@ -1,3 +1,4 @@ { liberaforms = import ./liberaforms.nix; + pretalx = import ./pretalx.nix; } diff --git a/modules/liberaforms.nix b/modules/liberaforms.nix index 25e8994b..475c7aa6 100644 --- a/modules/liberaforms.nix +++ b/modules/liberaforms.nix @@ -2,7 +2,6 @@ config, lib, pkgs, - ngipkgs, ... }: with lib; let @@ -25,7 +24,7 @@ in { package = mkOption { type = types.package; - default = ngipkgs.liberaforms; + default = pkgs.liberaforms; defaultText = literalExpression ".packages..default"; example = literalExpression "pkgs.liberaforms"; description = '' @@ -181,7 +180,7 @@ in { after = ["network.target" "postgresql.service"]; requires = ["postgresql.service"]; restartIfChanged = true; - path = with pkgs; [postgresql_11 ngipkgs.liberaforms-env openssl]; + path = with pkgs; [postgresql_11 liberaforms-env openssl]; serviceConfig = { Type = "simple"; @@ -273,7 +272,7 @@ in { # Do not edit this file, it is automatically generated by liberaforms.service. from dotenv import load_dotenv load_dotenv(dotenv_path="/var/lib/liberaforms/.env") - command = '${ngipkgs.liberaforms-env}/bin/gunicorn' + command = '${pkgs.liberaforms-env}/bin/gunicorn' bind = '${cfg.bind}' workers = ${toString cfg.workers} user = '${user}' @@ -325,7 +324,7 @@ in { psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE liberaforms TO liberaforms" flask db upgrade ''; - ExecStart = "${ngipkgs.liberaforms-env}/bin/gunicorn -c ${cfg.workDir}/gunicorn.py 'wsgi:create_app()'"; + ExecStart = "${pkgs.liberaforms-env}/bin/gunicorn -c ${cfg.workDir}/gunicorn.py 'wsgi:create_app()'"; ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; User = "${user}"; Group = "${group}"; diff --git a/modules/pretalx.nix b/modules/pretalx.nix new file mode 100644 index 00000000..c7de3186 --- /dev/null +++ b/modules/pretalx.nix @@ -0,0 +1,548 @@ +{ + config, + lib, + modulesPath, + options, + pkgs, + ... +}: +with builtins; +with lib; let + cfg = config.services.pretalx; + opt = options.services.pretalx; + gunicorn = pkgs.python3Packages.gunicorn; + libDir = "/var/lib/pretalx"; + gunicornSocketPath = "/var/run/pretalx.sock"; + gunicornSocket = "unix:${gunicornSocketPath}"; + pretalxWebServiceName = "pretalx-web"; + + # NOTE: This expect script might be replaced as soon as is resolved. + pretalxInit = pkgs.writeScriptBin "expect-pretalx-init" '' + #! ${pkgs.expect}/bin/expect -f + set timeout 10 + spawn ${cfg.package}/bin/pretalx init + log_user 0 + + expect "E-Mail: " + send -- "${cfg.init.admin.email}\n" + + set password [string trim [read [open "${cfg.init.admin.passwordFile}"]]] + + expect { + "Password: " { send -- "$password\n" } + "Error: That E-Mail is already taken." { puts "pretalx appears to be already initialized"; exit 0 } + } + + expect "Password (again): " + send -- "$password\n" + + expect { + -ex {Bypass password validation and create user anyway? [y/N]: } { puts "password is too weak"; exit 1 } + -ex {Name (e.g. "The Conference Organiser"): } { send -- "${cfg.init.organiser.name}\n" } + } + + expect -ex {Slug (e.g. "conforg", used in urls): } + send -- "${cfg.init.organiser.slug}\n" + + expect eof + ''; + + environmentFile = pkgs.runCommand "pretalx-environ" { + buildInputs = [cfg.package gunicorn]; # Sets PYTHONPATH in derivation + } "echo PYTHONPATH=$PYTHONPATH > $out;"; + + secretRecommendation = "Consider using a secret managing scheme such as `agenix` or `sops-nix` to generate this file."; +in { + options.services.pretalx = with types; { + enable = mkEnableOption "Enable pretalx server."; + + package = mkPackageOption pkgs "pretalx" {}; + + user = mkOption { + type = str; + description = "Username of the system user that should own files and services related to pretalx."; + default = "pretalx"; + }; + + group = mkOption { + type = str; + description = "Group that contains the system user that executes pretalx."; + default = "pretalx"; + }; + + nginx = mkOption { + type = types.submodule ( + recursiveUpdate + (import (modulesPath + "/services/web-servers/nginx/vhost-options.nix") {inherit config lib;}) {} + ); + default = {}; + example = literalExpression '' + { + serverAliases = [ + "pretalx.''${config.networking.domain}" + ]; + # To enable encryption and let let's encrypt take care of certificate + forceSSL = true; + enableACME = true; + } + ''; + description = "nginx virtualHost settings."; + }; + + gunicorn = { + extraArgs = mkOption { + type = str; + description = "Command line arguments passed to Gunicorn server."; + defaultText = literalExpression "\"--workers=4 --max-requests=1200 --max-requests-jitter=50 --log-level=error\""; + default = "--workers=4 --max-requests=1200 --max-requests-jitter=50 --log-level=error"; + }; + }; + + filesystem = { + data = mkOption { + type = path; + description = '' + Path that is the base for all other directories (see options `media`, `static`, `logs`). Unless you have a compelling reason to keep other files apart, setting this option is the easiest way to configure file storage. + ''; + default = libDir + "/data"; + }; + + media = mkOption { + type = str; + description = '' + Directory that contains user generated files. It needs to be writable by the pretalx process. + ''; + default = "${cfg.filesystem.data}/media"; + }; + + logs = mkOption { + type = str; + description = '' + Directory that contains logged data. It needs to be writable by the pretalx process. + ''; + default = "${cfg.filesystem.data}/logs"; + }; + + static = mkOption { + type = str; + description = '' + Directory that contains static files. It needs to be writable by the pretalx process. pretalx will put files there. + ''; + default = "${cfg.filesystem.data}/static"; + }; + }; + + site = let + cspOption = mkOption { + type = nullOr str; + description = '' + Use this setting to update the CSP security headers. + See . + ''; + default = null; + example = "https://example.com,'self'"; + }; + in { + url = mkOption { + type = str; + description = '' + URL for pretalx. pretalx uses this value when it has to render full URLs, for example in emails or feeds. It is also used to determine the allowed incoming hosts. + ''; + default = "http://${config.networking.fqdn}"; + example = "http://pretalx.example.com"; + }; + + secretFile = mkOption { + type = nullOr path; + description = "Path to a file containing a secret key that the Django web framework uses for cryptographic signing. See . ${secretRecommendation}"; + default = null; + example = "/run/secrets/pretalx/secret"; + }; + + media = mkOption { + type = str; + description = "Path that is appended to the site URL to address media files (all files uploaded by users or generated by pretalx)."; + default = "/media/"; + }; + + static = mkOption { + type = str; + description = "Path that is appended to the site URL to address static files."; + default = "/static/"; + }; + + csp = cspOption; + csp_script = cspOption; + csp_style = cspOption; + csp_img = cspOption; + csp_form = cspOption; + }; + + database = { + backend = mkOption { + type = enum ["postgresql" "mysql" "sqlite3"]; + description = '' + The default is SQLite ("sqlite3"), which is not a production + database. Please use a database like PostgreSQL ("postgresql") or MySQL ("mysql"). + ''; + default = "sqlite3"; + }; + + name = mkOption { + type = str; + description = "Database name. If you use SQLite, this is the filesystem path to the database file."; + default = "pretalx"; + }; + + user = mkOption { + type = nullOr str; + default = null; + example = "pretalx"; + }; + + passwordFile = mkOption { + type = nullOr path; + description = "Path to a file containing the database password. If you use PostgreSQL, consider using its peer authentication and not setting a password. ${secretRecommendation}"; + default = null; + example = "/run/secrets/pretalx/database"; + }; + + host = mkOption { + type = nullOr str; + description = "Database host, or path to a socket (if you use PostgreSQL or MySQL). For local PostgreSQL authentication, you can leave this variable empty."; + default = null; + example = "localhost"; + }; + + port = mkOption { + type = nullOr int; + description = "Database port (e.g. `5432` for PostgreSQL or `3306` for MySQL)."; + default = null; + example = "5432"; + }; + }; + + mail = { + enable = mkOption { + type = bool; + description = "Enable sending e-mails from pretalx."; + default = true; + }; + + from = mkOption { + type = str; + description = "Fall-back sender address, e.g. for when pretalx sends event-independent e-mails."; + default = "admin@localhost"; + }; + + host = mkOption { + type = str; + description = "Hostname of the SMTP server for sending e-mails."; + default = "localhost"; + }; + + port = mkOption { + type = int; + description = "TCP port of the SMTP server for sending e-mails."; + default = 25; + }; + + user = mkOption { + type = str; + description = "Username for SMTP server authentication."; + example = "admin"; + }; + + passwordFile = mkOption { + type = path; + description = "Path to a file containing the password for SMTP server authentication. ${secretRecommendation}"; + example = "/run/secrets/pretalx/mail"; + }; + + tls = mkOption { + type = bool; + description = "Whether to use TLS for sending mail."; + default = false; + }; + + ssl = mkOption { + type = bool; + description = "Whether to use SSL for sending mail."; + default = true; + }; + }; + + celery = { + enable = mkEnableOption "Enable support for Celery."; + + backendFile = mkOption { + type = nullOr path; + description = "Path to a file that contains the location (connection URI) of Celery backend. If you use a standard Redis-based setup, the file should contain `redis://127.0.0.1/1` or similar. Check the documentation . ${secretRecommendation}"; + default = null; + example = "/run/secrets/pretalx/celery-backend"; + }; + + brokerFile = mkOption { + type = nullOr path; + description = "Path to a file that contains the location (connection URI) of Celery broker. If you use a standard Redis-based setup, the file should contain `redis://127.0.0.1/2` or similar. Check the documentation . ${secretRecommendation}"; + default = null; + example = "/run/secrets/pretalx/celery-broker"; + }; + }; + + redis = { + enable = mkEnableOption "Enable support for Redis."; + + locationFile = mkOption { + type = path; + description = '' + Path to a file that contains the location (connection URI) of Redis server, if you want to use it as a cache. Contents of the file: `redis://[:password]@127.0.0.1:6379/1` would be sensible, or `unix://[:password]@/path/to/socket.sock?db=0` if you prefer to use sockets. ${secretRecommendation} + ''; + example = "/run/secrets/pretalx/redis"; + }; + + session = mkOption { + type = bool; + description = "Whether to use Redis as session storage."; + default = false; + }; + }; + + logging = { + enable = mkEnableOption "Enable support for logging."; + + email = mkOption { + type = str; + description = "E-mail address (or comma-separated list of addresses) to send system logs to."; + example = "root@example.com,admin@example.com"; + }; + + email_level = mkOption { + type = enum ["DEBUG" "INFO" "WARNING" "ERROR" "CRITICAL"]; + description = "Log level to start sending emails at."; + default = "ERROR"; + }; + }; + + locale = { + language_code = mkOption { + type = str; + description = "Default locale."; + default = "en"; + }; + time_zone = mkOption { + type = str; + description = '' + Default time zone as a `pytz` name. + + You can use following code to generate the full list of timezone names: + + ```python + import pytz + + print(pytz.all_timezones) + ``` + ''; + default = "UTC"; + }; + }; + + extraConfig = mkOption { + type = attrs; + description = '' + Extra configuration to be appended to the generated pretalx configuration file. + See for all options. + ''; + default = {}; + example = literalExpression "{ site.debug = true; }"; + }; + + init = { + admin = { + email = mkOption { + type = str; + description = "E-mail address of the administrator."; + example = "admin@example.com"; + }; + + passwordFile = mkOption { + type = path; + description = "Path to a file containing the administrator password. ${secretRecommendation}"; + example = "/run/secrets/pretalx/admin"; + }; + }; + + organiser = { + name = mkOption { + type = str; + description = "Name of the conference organiser."; + example = "The Conference Organiser"; + }; + + slug = mkOption { + type = str; + description = "Slug of the conference organiser (to be used in URLs)."; + example = "conforg"; + }; + }; + }; + }; + + config = mkIf cfg.enable { + assertions = [ + { + assertion = !(cfg.mail.tls && cfg.mail.ssl); + message = "Enable either `${opt.mail.tls}` or `${opt.mail.ssl}`."; + } + ]; + + services.nginx = { + enable = mkDefault true; + virtualHosts.${pretalxWebServiceName} = mkMerge [ + cfg.nginx + { + locations."/".proxyPass = "http://${pretalxWebServiceName}"; + } + ]; + upstreams.${pretalxWebServiceName}.servers."${gunicornSocket}".fail_timeout = "0"; + }; + + users.users."${cfg.user}" = { + isSystemUser = true; + createHome = true; + home = libDir; + group = cfg.group; + }; + + users.groups."${cfg.group}" = {}; + + environment.systemPackages = [ + cfg.package + pkgs.tmux + pkgs.htop + ]; + + environment.etc."pretalx/pretalx.cfg".text = let + hiddenNames = ["enable" "passwordFile" "locationFile" "backendFile" "brokerFile" "secretFile"]; + ifEnable = s: + if s.enable + then s + else {}; + pretalxCfg = + filterAttrs (n: v: + v != {}) # Removes empty attrsets, otherwise `generators.toINI` will fail. + + (filterAttrsRecursive (n: v: (!(elem n hiddenNames) && v != null)) { + inherit (cfg) filesystem site database locale; + + celery = ifEnable cfg.celery; + logging = ifEnable cfg.logging; + mail = ifEnable cfg.mail; + redis = ifEnable cfg.redis; + }); + in + generators.toINI {} (recursiveUpdate pretalxCfg cfg.extraConfig); + + systemd = { + sockets."pretalx-web" = { + listenStreams = [gunicornSocketPath]; + socketConfig.SocketUser = config.services.nginx.user; + wantedBy = ["sockets.target"]; + }; + services = let + catFile = varname: filename: "export ${varname}=\"$(cat ${filename})\""; + exportPasswordEnv = lib.concatStringsSep "\n" ([] + ++ optional cfg.mail.enable (catFile "PRETALX_MAIL_PASSWORD" cfg.mail.passwordFile) + ++ optional (cfg.database.passwordFile != null) (catFile "PRETALX_DB_PASS" cfg.database.passwordFile) + ++ optional cfg.redis.enable (catFile "PRETALX_REDIS" cfg.redis.locationFile) + ++ optional (cfg.site.secretFile != null) (catFile "SECRET_KEY" cfg.site.secretFile) + ++ optional cfg.celery.enable '' + ${catFile "PRETALX_CELERY_BACKEND" cfg.celery.backendFile} + ${catFile "PRETALX_CELERY_BROKER" cfg.celery.brokerFile} + ''); + oneshotServiceConfig = { + Type = "oneshot"; + EnvironmentFile = environmentFile; + User = cfg.user; + Group = cfg.group; + }; + mkOneshot = command: { + serviceConfig = oneshotServiceConfig; + script = '' + ${exportPasswordEnv} + ${cfg.package}/bin/pretalx ${command} + ''; + }; + in { + ${pretalxWebServiceName} = { + serviceConfig = { + Type = "notify"; + Restart = "on-failure"; + EnvironmentFile = environmentFile; + User = cfg.user; + Group = cfg.group; + }; + script = '' + ${exportPasswordEnv} + + # ${cfg.package}/bin/pretalx compilemessages # FIXME: when run, pretalx-web hangs + ${cfg.package}/bin/pretalx collectstatic --noinput + ${cfg.package}/bin/pretalx compress + + exec ${gunicorn}/bin/gunicorn pretalx.wsgi --name=${pretalxWebServiceName} --bind=${gunicornSocket} ${cfg.gunicorn.extraArgs} + ''; + wantedBy = ["multi-user.target"]; + requires = ["pretalx-init.service" "pretalx-web.socket"]; + after = ["pretalx-init.service"]; + }; + + pretalx-init = { + serviceConfig = oneshotServiceConfig; + script = '' + ${exportPasswordEnv} + ${pretalxInit}/bin/expect-pretalx-init + ''; + requires = ["pretalx-migrate.service"]; + after = ["network.target" "pretalx-migrate.service"]; + }; + + pretalx-migrate = mkOneshot "migrate"; + pretalx-rebuild = mkOneshot "rebuild"; + pretalx-clearsessions = mkOneshot "clearsessions"; + pretalx-runperiodic = mkOneshot "runperiodic"; + }; + + timers = let + mkTimer = { + description, + unit, + onCalendar, + }: { + inherit description; + requires = ["pretalx-web.service"]; + after = ["network.target" "pretalx-web.service"]; + wantedBy = ["timers.target"]; + timerConfig = { + Persistent = true; + OnCalendar = onCalendar; + Unit = unit; + }; + }; + in { + # About once a month + pretalx-clearsessions = mkTimer { + description = "Clear pretalx sessions"; + unit = "pretalx-clearsessions.service"; + onCalendar = "monthly"; + }; + + # Once every 15 minutes + pretalx-runperiodic = mkTimer { + description = "Run pretalx tasks"; + unit = "pretalx-runperiodic.service"; + onCalendar = "*:0/15"; + }; + }; + }; + }; +} diff --git a/pkgs/pretalx/README.md b/pkgs/pretalx/README.md new file mode 100644 index 00000000..df385126 --- /dev/null +++ b/pkgs/pretalx/README.md @@ -0,0 +1,57 @@ +# Pretalx Service + +This provides [pretalx][pretalx]. + +## Production Deployment + +The configuration at `/configs/pretalx/pretalx.nix` and `/config/pretalx/{mysql,postgresql}.nix` serves as a starting point for a production-ready configuration to deploy pretalx. + +### Secret Management + +It is recommended to use a secret management scheme for security (see [this comparison][secret-management-comparison]). +In the example configuration in `/configs/pretalx/pretalx.nix`, we use [sops-nix][sops-nix] with [age][age] for the options `services.pretalx.*.*File`. + +### TLS Certificates for HTTPS + +In order to enable HTTPS, you can obtain the TLS certificates via ACME. See the [NixOS manual][nixos-manual-acme] for details. + +## Packaging + +### Pretalx Version Update + +* Update the version of `pretalx` in `tool.poetry.dependencies` section of `/pkgs/pretalx/pyproject.toml` file. +* Run `nix run nixpkgs#poetry -- lock` in `/pkgs/pretalx`. This will update `/pkgs/pretalx/poetry.lock`. +* Build the package via `nix build .#pretalx`. If this fails, refer to `poetry2nix` documentation, e.g. to [Edge cases][poetry2nix-edge] if the error is "missing `setuptools`" or similar, and manually apply overrides to repair dependencies. Check `/pkgs/pretalx/default.nix` for examples. + +### Testing + +Run NixOS integration tests using +```sh +nix build -L .#nixosTests.x86_64-linux.pretalx +``` + +#### Interactive Tests + +Run NixOS integration tests interactively using +```sh +nix build -L .#nixosTests.x86_64-linux.pretalx.driverInteractive +./result/bin/nixos-test-driver # Start a shell +``` + +Once in the spawned shell, you can start a VM that will execute the tests using the following command: +```python +start_all() # Run the VM +``` + +If you want to use your browser to connect to the `pretalx` server at , you may set up port forwarding as follows: +```python +server.forward_port(host_port=8000, guest_port=80) +``` + +[pretalx]: https://github.com/pretalx/pretalx +[poetry2nix-edge]: https://github.com/nix-community/poetry2nix/blob/master/docs/edgecases.md +[secret-management-comparison]: https://nixos.wiki/wiki/Comparison_of_secret_managing_schemes +[sops-nix]: https://github.com/Mic92/sops-nix +[age]: https://github.com/FiloSottile/age +[nixos-manual-acme]: https://nixos.org/manual/nixos/stable/#module-security-acme-nginx + diff --git a/pkgs/pretalx/celery.patch b/pkgs/pretalx/celery.patch new file mode 100644 index 00000000..c72cefb3 --- /dev/null +++ b/pkgs/pretalx/celery.patch @@ -0,0 +1,6 @@ +diff --git a/requirements/default.txt b/requirements/default.txt +--- a/requirements/default.txt ++++ b/requirements/default.txt +@@ -1,1 +1,1 @@ +-pytz>dev ++pytz diff --git a/pkgs/pretalx/default.nix b/pkgs/pretalx/default.nix new file mode 100644 index 00000000..a35a2932 --- /dev/null +++ b/pkgs/pretalx/default.nix @@ -0,0 +1,77 @@ +{ + lib, + gettext, + pkg-config, + poetry2nix, + libmysqlclient, + withMysql ? false, + withPostgresql ? false, + withRedis ? false, + withTest ? false, +}: let + pname = "pretalx"; + version = "2.3.2"; +in (poetry2nix.mkPoetryApplication { + projectDir = ./.; + propagatedBuildInputs = [gettext]; + groups = + [] + ++ lib.optional withMysql "mysql" + ++ lib.optional withPostgresql "postgresql" + ++ lib.optional withRedis "redis" + ++ lib.optional withTest "test"; + + overrides = poetry2nix.overrides.withDefaults (self: super: let + addSetupTools = old: { + propagatedBuildInputs = + (old.propagatedBuildInputs or []) + ++ [self.setuptools]; + }; + in { + defusedcsv = super.defusedcsv.overridePythonAttrs addSetupTools; + django-context-decorator = + super.django-context-decorator.overridePythonAttrs addSetupTools; + django-i18nfield = + super.django-i18nfield.overridePythonAttrs addSetupTools; + publicsuffixlist = + super.publicsuffixlist.overridePythonAttrs addSetupTools; + rules = super.rules.overridePythonAttrs addSetupTools; + urlman = super.urlman.overridePythonAttrs addSetupTools; + kombu = + super.kombu.overridePythonAttrs + (old: (addSetupTools old) // {patches = [./kombu.patch];}); + celery = + super.celery.overridePythonAttrs + (old: {patches = [./celery.patch];}); + django-hierarkey = + super.django-hierarkey.overridePythonAttrs addSetupTools; + django-jquery-js = + super.django-jquery-js.overridePythonAttrs addSetupTools; + django-scopes = super.django-scopes.overridePythonAttrs addSetupTools; + django-bootstrap4 = + super.django-bootstrap4.overridePythonAttrs addSetupTools; + inlinestyler = super.inlinestyler.overridePythonAttrs addSetupTools; + django-libsass = super.django-libsass.overridePythonAttrs addSetupTools; + mysqlclient = super.mysqlclient.overridePythonAttrs (old: { + nativeBuildInputs = + (old.nativeBuildInputs or []) + ++ [libmysqlclient pkg-config]; + buildInputs = (old.buildInputs or []) ++ [libmysqlclient]; + }); + }); + + meta = with lib; { + description = "pretalx is a conference management software"; + homepage = "https://pretalx.com"; + changelog = "https://docs.pretalx.org/changelog.html#${version}"; + license = licenses.asl20; + platforms = platforms.linux; + maintainers = with maintainers; + [ + andresnav + imincik + lorenzleutgeb + ] + ++ (with (import ../../maintainers/maintainers-list.nix); [augustebaum kubaneko]); + }; +}) diff --git a/pkgs/pretalx/kombu.patch b/pkgs/pretalx/kombu.patch new file mode 100644 index 00000000..c98004e7 --- /dev/null +++ b/pkgs/pretalx/kombu.patch @@ -0,0 +1,6 @@ +diff --git a/requirements/test.txt b/requirements/test.txt +--- a/requirements/test.txt ++++ b/requirements/test.txt +@@ -1,1 +1,1 @@ +-pytz>dev ++pytz diff --git a/pkgs/pretalx/poetry.lock b/pkgs/pretalx/poetry.lock new file mode 100644 index 00000000..ce4e347e --- /dev/null +++ b/pkgs/pretalx/poetry.lock @@ -0,0 +1,1262 @@ +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. + +[[package]] +name = "amqp" +version = "2.6.1" +description = "Low-level AMQP client for Python (fork of amqplib)." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "amqp-2.6.1-py2.py3-none-any.whl", hash = "sha256:aa7f313fb887c91f15474c1229907a04dac0b8135822d6603437803424c0aa59"}, + {file = "amqp-2.6.1.tar.gz", hash = "sha256:70cdb10628468ff14e57ec2f751c7aa9e48e7e3651cfd62d431213c0c4e58f21"}, +] + +[package.dependencies] +vine = ">=1.1.3,<5.0.0a1" + +[[package]] +name = "asgiref" +version = "3.7.2" +description = "ASGI specs, helper code, and adapters" +optional = false +python-versions = ">=3.7" +files = [ + {file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"}, + {file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} + +[package.extras] +tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] + +[[package]] +name = "async-timeout" +version = "4.0.3" +description = "Timeout context manager for asyncio programs" +optional = false +python-versions = ">=3.7" +files = [ + {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, + {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, +] + +[[package]] +name = "beautifulsoup4" +version = "4.10.0" +description = "Screen-scraping library" +optional = false +python-versions = ">3.0.0" +files = [ + {file = "beautifulsoup4-4.10.0-py3-none-any.whl", hash = "sha256:9a315ce70049920ea4572a4055bc4bd700c940521d36fc858205ad4fcde149bf"}, + {file = "beautifulsoup4-4.10.0.tar.gz", hash = "sha256:c23ad23c521d818955a4151a67d81580319d4bf548d3d49f4223ae041ff98891"}, +] + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "billiard" +version = "3.6.4.0" +description = "Python multiprocessing fork with improvements and bugfixes" +optional = false +python-versions = "*" +files = [ + {file = "billiard-3.6.4.0-py3-none-any.whl", hash = "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"}, + {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, +] + +[[package]] +name = "bleach" +version = "4.1.0" +description = "An easy safelist-based HTML-sanitizing tool." +optional = false +python-versions = ">=3.6" +files = [ + {file = "bleach-4.1.0-py2.py3-none-any.whl", hash = "sha256:4d2651ab93271d1129ac9cbc679f524565cc8a1b791909c4a51eac4446a15994"}, + {file = "bleach-4.1.0.tar.gz", hash = "sha256:0900d8b37eba61a802ee40ac0061f8c2b5dee29c1927dd1d233e075ebf5a71da"}, +] + +[package.dependencies] +packaging = "*" +six = ">=1.9.0" +webencodings = "*" + +[[package]] +name = "celery" +version = "4.4.7" +description = "Distributed Task Queue." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "celery-4.4.7-py2.py3-none-any.whl", hash = "sha256:a92e1d56e650781fb747032a3997d16236d037c8199eacd5217d1a72893bca45"}, + {file = "celery-4.4.7.tar.gz", hash = "sha256:d220b13a8ed57c78149acf82c006785356071844afe0b27012a4991d44026f9f"}, +] + +[package.dependencies] +billiard = ">=3.6.3.0,<4.0" +kombu = ">=4.6.10,<4.7" +pytz = ">0.0-dev" +vine = "1.3.0" + +[package.extras] +arangodb = ["pyArango (>=1.3.2)"] +auth = ["cryptography"] +azureblockblob = ["azure-common (==1.1.5)", "azure-storage (==0.36.0)", "azure-storage-common (==1.1.0)"] +brotli = ["brotli (>=1.0.0)", "brotlipy (>=0.7.0)"] +cassandra = ["cassandra-driver (<3.21.0)"] +consul = ["python-consul"] +cosmosdbsql = ["pydocumentdb (==2.3.2)"] +couchbase = ["couchbase (<3.0.0)", "couchbase-cffi (<3.0.0)"] +couchdb = ["pycouchdb"] +django = ["Django (>=1.11)"] +dynamodb = ["boto3 (>=1.9.178)"] +elasticsearch = ["elasticsearch"] +eventlet = ["eventlet (>=0.24.1)"] +gevent = ["gevent"] +librabbitmq = ["librabbitmq (>=1.5.0)"] +lzma = ["backports.lzma"] +memcache = ["pylibmc"] +mongodb = ["pymongo[srv] (>=3.3.0)"] +msgpack = ["msgpack"] +pymemcache = ["python-memcached"] +pyro = ["pyro4"] +redis = ["redis (>=3.2.0)"] +riak = ["riak (>=2.0)"] +s3 = ["boto3 (>=1.9.125)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +solar = ["ephem"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.9.125)", "pycurl (==7.43.0.5)"] +tblib = ["tblib (>=1.3.0)", "tblib (>=1.5.0)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] +zstd = ["zstandard"] + +[[package]] +name = "certifi" +version = "2023.7.22" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"}, + {file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"}, +] + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.5.0" +files = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] + +[package.extras] +unicode-backport = ["unicodedata2"] + +[[package]] +name = "colorama" +version = "0.4.6" +description = "Cross-platform colored terminal text." +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] + +[[package]] +name = "csscompressor" +version = "0.9.5" +description = "A python port of YUI CSS Compressor" +optional = false +python-versions = "*" +files = [ + {file = "csscompressor-0.9.5.tar.gz", hash = "sha256:afa22badbcf3120a4f392e4d22f9fff485c044a1feda4a950ecc5eba9dd31a05"}, +] + +[[package]] +name = "cssutils" +version = "2.3.1" +description = "A CSS Cascading Style Sheets library for Python" +optional = false +python-versions = ">=3.6" +files = [ + {file = "cssutils-2.3.1-py3-none-any.whl", hash = "sha256:aef263babe9738161cd75214e63e5755346017835ce60f4cfe0987c17f0f4b68"}, + {file = "cssutils-2.3.1.tar.gz", hash = "sha256:5ea7892019d81dec2216c15b03d6ecc7610cfc60782a1bc9d0fe3adf6a3b8c6d"}, +] + +[package.extras] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["cssselect", "importlib-resources", "lxml", "mock", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] + +[[package]] +name = "defusedcsv" +version = "1.1.0" +description = "Drop-in replacement for Python's CSV library that tries to mitigate CSV injection attacks" +optional = false +python-versions = "*" +files = [ + {file = "defusedcsv-1.1.0-py3-none-any.whl", hash = "sha256:6c623b21a03a0f0ef8caa95bdd36767de0be6c96019f85cfc2646483032127e4"}, + {file = "defusedcsv-1.1.0.tar.gz", hash = "sha256:ad17cd4bd85b7ac6172e7100c56e07899f41439859f02a1567b0ce5a85f864e9"}, +] + +[[package]] +name = "django" +version = "3.2.20" +description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Django-3.2.20-py3-none-any.whl", hash = "sha256:a477ab326ae7d8807dc25c186b951ab8c7648a3a23f9497763c37307a2b5ef87"}, + {file = "Django-3.2.20.tar.gz", hash = "sha256:dec2a116787b8e14962014bf78e120bba454135108e1af9e9b91ade7b2964c40"}, +] + +[package.dependencies] +asgiref = ">=3.3.2,<4" +pytz = "*" +sqlparse = ">=0.2.2" + +[package.extras] +argon2 = ["argon2-cffi (>=19.1.0)"] +bcrypt = ["bcrypt"] + +[[package]] +name = "django-appconf" +version = "1.0.5" +description = "A helper class for handling configuration defaults of packaged apps gracefully." +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, + {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, +] + +[package.dependencies] +django = "*" + +[[package]] +name = "django-bootstrap4" +version = "3.0.1" +description = "Bootstrap 4 for Django" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-bootstrap4-3.0.1.tar.gz", hash = "sha256:c5c97fb473bb56e3a91b4f4be52b74a3fc384ec3baae50dd0807fa922a55ec2b"}, + {file = "django_bootstrap4-3.0.1-py3-none-any.whl", hash = "sha256:aa8a9cb5ab27cfae52a27d377a0401af268d0e4b91a5f8e660546464582cc010"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.8.0" +Django = ">=2.2" + +[[package]] +name = "django-compressor" +version = "3.1" +description = "Compresses linked and inline JavaScript or CSS into single cached files." +optional = false +python-versions = "*" +files = [ + {file = "django_compressor-3.1-py2.py3-none-any.whl", hash = "sha256:89f7ba86777b30672c2f9c7557bf2aff87c5890903c73b1fa3ae38acd143e855"}, + {file = "django_compressor-3.1.tar.gz", hash = "sha256:c4a87bf65f9a534cfaf1c321a000a229c24e50c6d62ba6ab089482db42e819d9"}, +] + +[package.dependencies] +django-appconf = ">=1.0.3" +rcssmin = "1.1.0" +rjsmin = "1.2.0" + +[[package]] +name = "django-context-decorator" +version = "1.6.0" +description = "A context decorator for Django views" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django_context_decorator-1.6.0-py3-none-any.whl", hash = "sha256:6b4ba53811362c359221da243dd8db19c02891bb493e984e4dbe5edfd54f40b5"}, + {file = "django_context_decorator-1.6.0.tar.gz", hash = "sha256:4a70acd422165807710873cfa5e0cbf9e24ad2548b065f726e404f9473997e29"}, +] + +[[package]] +name = "django-countries" +version = "7.5.1" +description = "Provides a country field for Django models." +optional = false +python-versions = "*" +files = [ + {file = "django-countries-7.5.1.tar.gz", hash = "sha256:22915d9b9403932b731622619940a54894a3eb0da9a374e7249c8fc453c122d7"}, + {file = "django_countries-7.5.1-py3-none-any.whl", hash = "sha256:2df707aca7a5e677254bed116cf6021a136ebaccd5c2f46860abd6452bb45521"}, +] + +[package.dependencies] +asgiref = "*" +typing-extensions = "*" + +[package.extras] +dev = ["black", "django", "djangorestframework", "graphene-django", "pytest", "pytest-django", "tox"] +maintainer = ["django", "transifex-client", "zest.releaser[recommended]"] +pyuca = ["pyuca"] +test = ["djangorestframework", "graphene-django", "pytest", "pytest-cov", "pytest-django"] + +[[package]] +name = "django-csp" +version = "3.7" +description = "Django Content Security Policy support." +optional = false +python-versions = "*" +files = [ + {file = "django_csp-3.7-py2.py3-none-any.whl", hash = "sha256:01443a07723f9a479d498bd7bb63571aaa771e690f64bde515db6cdb76e8041a"}, + {file = "django_csp-3.7.tar.gz", hash = "sha256:01eda02ad3f10261c74131cdc0b5a6a62b7c7ad4fd017fbefb7a14776e0a9727"}, +] + +[package.dependencies] +Django = ">=1.8" + +[package.extras] +jinja2 = ["jinja2 (>=2.9.6)"] +tests = ["jinja2 (>=2.9.6)", "mock (==1.0.1)", "pep8 (==1.4.6)", "pytest (<4.0)", "pytest-django", "pytest-flakes (==1.0.1)", "pytest-pep8 (==1.0.6)", "six (==1.12.0)"] + +[[package]] +name = "django-filter" +version = "21.1" +description = "Django-filter is a reusable Django application for allowing users to filter querysets dynamically." +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-filter-21.1.tar.gz", hash = "sha256:632a251fa8f1aadb4b8cceff932bb52fe2f826dd7dfe7f3eac40e5c463d6836e"}, + {file = "django_filter-21.1-py3-none-any.whl", hash = "sha256:f4a6737a30104c98d2e2a5fb93043f36dd7978e0c7ddc92f5998e85433ea5063"}, +] + +[package.dependencies] +Django = ">=2.2" + +[[package]] +name = "django-formset-js-improved" +version = "0.5.0.2" +description = "Extend Django formsets with JavaScript" +optional = false +python-versions = "*" +files = [ + {file = "django-formset-js-improved-0.5.0.2.tar.gz", hash = "sha256:251649ca389144728359b57d5845c601b3420fdafa2e4634668de614e14953ce"}, +] + +[package.dependencies] +Django = "*" +django-jquery-js = "*" + +[[package]] +name = "django-formtools" +version = "2.3" +description = "A set of high-level abstractions for Django forms" +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-formtools-2.3.tar.gz", hash = "sha256:9663b6eca64777b68d6d4142efad8597fe9a685924673b25aa8a1dcff4db00c3"}, + {file = "django_formtools-2.3-py3-none-any.whl", hash = "sha256:4699937e19ee041d803943714fe0c1c7ad4cab802600eb64bbf4cdd0a1bfe7d9"}, +] + +[package.dependencies] +Django = ">=2.2" + +[[package]] +name = "django-hierarkey" +version = "1.0.4" +description = "Hierarchical key-value store for django" +optional = false +python-versions = "*" +files = [ + {file = "django-hierarkey-1.0.4.tar.gz", hash = "sha256:f90798ec2bcb3aae23a3468a9c1977621afa48f302a417d17bc85fbe9f284d75"}, + {file = "django_hierarkey-1.0.4-py3-none-any.whl", hash = "sha256:80c596287d1b4256a96febf2a5f888141eee9621d9a917b659054d8fe746dc6c"}, +] + +[package.dependencies] +python-dateutil = "*" + +[[package]] +name = "django-i18nfield" +version = "1.9.4" +description = "Store internationalized strings in Django models" +optional = false +python-versions = "*" +files = [ + {file = "django-i18nfield-1.9.4.tar.gz", hash = "sha256:f24c209f4fcbf407ec1ebf749b6c182bf4089ef46c3cd0e60fa547955d575d2e"}, + {file = "django_i18nfield-1.9.4-py3-none-any.whl", hash = "sha256:d31bf725b43d6fa0d67a403efb45abf54fc6013ee9135ad55679626df9d0c8ca"}, +] + +[[package]] +name = "django-jquery-js" +version = "3.1.1" +description = "jQuery, bundled up so apps can depend upon it" +optional = false +python-versions = "*" +files = [ + {file = "django-jquery-js-3.1.1.tar.gz", hash = "sha256:308e6472801f89be7c02fa3d06bea6470cfbcab8287db80c64b1093717b8eea9"}, +] + +[package.dependencies] +Django = ">=1.4" + +[[package]] +name = "django-libsass" +version = "0.9" +description = "A django-compressor filter to compile SASS files using libsass" +optional = false +python-versions = "*" +files = [ + {file = "django-libsass-0.9.tar.gz", hash = "sha256:bfbbb55a8950bb40fa04dd416605f92da34ad1f303b10a41abc3232386ec27b5"}, + {file = "django_libsass-0.9-py3-none-any.whl", hash = "sha256:5234d29100889cac79e36a0f44207ec6d275adfd2da1acb6a94b55c89fe2bd97"}, +] + +[package.dependencies] +django-compressor = ">=1.3" +libsass = ">=0.7.0,<1" + +[[package]] +name = "django-redis" +version = "5.3.0" +description = "Full featured redis cache backend for Django." +optional = false +python-versions = ">=3.6" +files = [ + {file = "django-redis-5.3.0.tar.gz", hash = "sha256:8bc5793ec06b28ea802aad85ec437e7646511d4e571e07ccad19cfed8b9ddd44"}, + {file = "django_redis-5.3.0-py3-none-any.whl", hash = "sha256:2d8660d39f586c41c9907d5395693c477434141690fd7eca9d32376af00b0aac"}, +] + +[package.dependencies] +Django = ">=3.2" +redis = ">=3,<4.0.0 || >4.0.0,<4.0.1 || >4.0.1" + +[package.extras] +hiredis = ["redis[hiredis] (>=3,!=4.0.0,!=4.0.1)"] + +[[package]] +name = "django-scopes" +version = "1.2.0.post1" +description = "Scope querys in multi-tenant django applications" +optional = false +python-versions = "*" +files = [ + {file = "django-scopes-1.2.0.post1.tar.gz", hash = "sha256:73b6d434f03b7cb8ea0bd7a39c730bab03f4729a300660817d89b4c3a8e1f4ad"}, + {file = "django_scopes-1.2.0.post1-py3-none-any.whl", hash = "sha256:60667d7d491c72b8ba912b6eaddeb0bc944fca5d19b5964389540bf71abe42c8"}, +] + +[package.dependencies] +Django = ">=3.2" + +[[package]] +name = "djangorestframework" +version = "3.13.1" +description = "Web APIs for Django, made easy." +optional = false +python-versions = ">=3.6" +files = [ + {file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"}, + {file = "djangorestframework-3.13.1.tar.gz", hash = "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee"}, +] + +[package.dependencies] +django = ">=2.2" +pytz = "*" + +[[package]] +name = "faker" +version = "19.3.0" +description = "Faker is a Python package that generates fake data for you." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Faker-19.3.0-py3-none-any.whl", hash = "sha256:bee54278d6e1289573317604ab6f4782acca724396bf261eaf1890de228e553d"}, + {file = "Faker-19.3.0.tar.gz", hash = "sha256:7d6ed00de3eef9bd57504500c67ee034cab959e4248f9c24aca33e08af82ca93"}, +] + +[package.dependencies] +python-dateutil = ">=2.4" + +[[package]] +name = "freezegun" +version = "1.2.2" +description = "Let your Python tests travel through time" +optional = false +python-versions = ">=3.6" +files = [ + {file = "freezegun-1.2.2-py3-none-any.whl", hash = "sha256:ea1b963b993cb9ea195adbd893a48d573fda951b0da64f60883d7e988b606c9f"}, + {file = "freezegun-1.2.2.tar.gz", hash = "sha256:cd22d1ba06941384410cd967d8a99d5ae2442f57dfafeff2fda5de8dc5c05446"}, +] + +[package.dependencies] +python-dateutil = ">=2.7" + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] + +[[package]] +name = "inlinestyler" +version = "0.2.5" +description = "Inlines external CSS into HTML elements." +optional = false +python-versions = "*" +files = [ + {file = "inlinestyler-0.2.5-py2.py3-none-any.whl", hash = "sha256:7cc301bdca944c9a09ae5d57a761af7b94d2ebc93aa9699ba67b12bd4d985b08"}, + {file = "inlinestyler-0.2.5.tar.gz", hash = "sha256:49ff2d03bc848ab6edc1123421efb0264eb4f53bf5bad22685039fda98ee5a05"}, +] + +[package.dependencies] +cssutils = "*" +lxml = "*" +requests = ">=1.0.0" + +[[package]] +name = "kombu" +version = "4.6.11" +description = "Messaging library for Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "kombu-4.6.11-py2.py3-none-any.whl", hash = "sha256:be48cdffb54a2194d93ad6533d73f69408486483d189fe9f5990ee24255b0e0a"}, + {file = "kombu-4.6.11.tar.gz", hash = "sha256:ca1b45faac8c0b18493d02a8571792f3c40291cf2bcf1f55afed3d8f3aa7ba74"}, +] + +[package.dependencies] +amqp = ">=2.6.0,<2.7" + +[package.extras] +azureservicebus = ["azure-servicebus (>=0.21.1)"] +azurestoragequeues = ["azure-storage-queue"] +consul = ["python-consul (>=0.6.0)"] +librabbitmq = ["librabbitmq (>=1.5.2)"] +mongodb = ["pymongo (>=3.3.0)"] +msgpack = ["msgpack"] +pyro = ["pyro4"] +qpid = ["qpid-python (>=0.26)", "qpid-tools (>=0.26)"] +redis = ["redis (>=3.3.11)"] +slmq = ["softlayer-messaging (>=1.0.3)"] +sqlalchemy = ["sqlalchemy"] +sqs = ["boto3 (>=1.4.4)", "pycurl (==7.43.0.2)"] +yaml = ["PyYAML (>=3.10)"] +zookeeper = ["kazoo (>=1.3.1)"] + +[[package]] +name = "libsass" +version = "0.21.0" +description = "Sass for Python: A straightforward binding of libsass for Python." +optional = false +python-versions = "*" +files = [ + {file = "libsass-0.21.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:06c8776417fe930714bdc930a3d7e795ae3d72be6ac883ff72a1b8f7c49e5ffb"}, + {file = "libsass-0.21.0-cp27-cp27m-win32.whl", hash = "sha256:a005f298f64624f313a3ac618ab03f844c71d84ae4f4a4aec4b68d2a4ffe75eb"}, + {file = "libsass-0.21.0-cp27-cp27m-win_amd64.whl", hash = "sha256:6b984510ed94993708c0d697b4fef2d118929bbfffc3b90037be0f5ccadf55e7"}, + {file = "libsass-0.21.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e25dd9047a9392d3c59a0b869e0404f2b325a03871ee45285ee33b3664f5613"}, + {file = "libsass-0.21.0-cp36-abi3-macosx_10_14_x86_64.whl", hash = "sha256:12f39712de38689a8b785b7db41d3ba2ea1d46f9379d81ea4595802d91fa6529"}, + {file = "libsass-0.21.0-cp36-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e2b1a7d093f2e76dc694c17c0c285e846d0b0deb0e8b21dc852ba1a3a4e2f1d6"}, + {file = "libsass-0.21.0-cp36-abi3-win32.whl", hash = "sha256:abc29357ee540849faf1383e1746d40d69ed5cb6d4c346df276b258f5aa8977a"}, + {file = "libsass-0.21.0-cp36-abi3-win_amd64.whl", hash = "sha256:659ae41af8708681fa3ec73f47b9735a6725e71c3b66ff570bfce78952f2314e"}, + {file = "libsass-0.21.0-cp38-abi3-macosx_12_0_arm64.whl", hash = "sha256:c9ec490609752c1d81ff6290da33485aa7cb6d7365ac665b74464c1b7d97f7da"}, + {file = "libsass-0.21.0.tar.gz", hash = "sha256:d5ba529d9ce668be9380563279f3ffe988f27bc5b299c5a28453df2e0b0fbaf2"}, +] + +[package.dependencies] +six = "*" + +[[package]] +name = "lxml" +version = "4.9.3" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" +files = [ + {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, + {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, + {file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"}, + {file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, + {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, + {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, + {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, + {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1247694b26342a7bf47c02e513d32225ededd18045264d40758abeb3c838a51f"}, + {file = "lxml-4.9.3-cp310-cp310-win32.whl", hash = "sha256:cdb650fc86227eba20de1a29d4b2c1bfe139dc75a0669270033cb2ea3d391b85"}, + {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, + {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, + {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, + {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e28c51fa0ce5674be9f560c6761c1b441631901993f76700b1b30ca6c8378d6"}, + {file = "lxml-4.9.3-cp311-cp311-win32.whl", hash = "sha256:0bfd0767c5c1de2551a120673b72e5d4b628737cb05414f03c3277bf9bed3305"}, + {file = "lxml-4.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:25f32acefac14ef7bd53e4218fe93b804ef6f6b92ffdb4322bb6d49d94cad2bc"}, + {file = "lxml-4.9.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:d3ff32724f98fbbbfa9f49d82852b159e9784d6094983d9a8b7f2ddaebb063d4"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:48d6ed886b343d11493129e019da91d4039826794a3e3027321c56d9e71505be"}, + {file = "lxml-4.9.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9a92d3faef50658dd2c5470af249985782bf754c4e18e15afb67d3ab06233f13"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b4e4bc18382088514ebde9328da057775055940a1f2e18f6ad2d78aa0f3ec5b9"}, + {file = "lxml-4.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fc9b106a1bf918db68619fdcd6d5ad4f972fdd19c01d19bdb6bf63f3589a9ec5"}, + {file = "lxml-4.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:d37017287a7adb6ab77e1c5bee9bcf9660f90ff445042b790402a654d2ad81d8"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56dc1f1ebccc656d1b3ed288f11e27172a01503fc016bcabdcbc0978b19352b7"}, + {file = "lxml-4.9.3-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:578695735c5a3f51569810dfebd05dd6f888147a34f0f98d4bb27e92b76e05c2"}, + {file = "lxml-4.9.3-cp35-cp35m-win32.whl", hash = "sha256:704f61ba8c1283c71b16135caf697557f5ecf3e74d9e453233e4771d68a1f42d"}, + {file = "lxml-4.9.3-cp35-cp35m-win_amd64.whl", hash = "sha256:c41bfca0bd3532d53d16fd34d20806d5c2b1ace22a2f2e4c0008570bf2c58833"}, + {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, + {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0c0850c8b02c298d3c7006b23e98249515ac57430e16a166873fc47a5d549287"}, + {file = "lxml-4.9.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:aca086dc5f9ef98c512bac8efea4483eb84abbf926eaeedf7b91479feb092458"}, + {file = "lxml-4.9.3-cp36-cp36m-win32.whl", hash = "sha256:50baa9c1c47efcaef189f31e3d00d697c6d4afda5c3cde0302d063492ff9b477"}, + {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, + {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:081d32421db5df44c41b7f08a334a090a545c54ba977e47fd7cc2deece78809a"}, + {file = "lxml-4.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23eed6d7b1a3336ad92d8e39d4bfe09073c31bfe502f20ca5116b2a334f8ec02"}, + {file = "lxml-4.9.3-cp37-cp37m-win32.whl", hash = "sha256:1509dd12b773c02acd154582088820893109f6ca27ef7291b003d0e81666109f"}, + {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, + {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3e9bdd30efde2b9ccfa9cb5768ba04fe71b018a25ea093379c857c9dad262c40"}, + {file = "lxml-4.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fcdd00edfd0a3001e0181eab3e63bd5c74ad3e67152c84f93f13769a40e073a7"}, + {file = "lxml-4.9.3-cp38-cp38-win32.whl", hash = "sha256:57aba1bbdf450b726d58b2aea5fe47c7875f5afb2c4a23784ed78f19a0462574"}, + {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, + {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, + {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b6420a005548ad52154c8ceab4a1290ff78d757f9e5cbc68f8c77089acd3c432"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bb3bb49c7a6ad9d981d734ef7c7193bc349ac338776a0360cc671eaee89bcf69"}, + {file = "lxml-4.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d27be7405547d1f958b60837dc4c1007da90b8b23f54ba1f8b728c78fdb19d50"}, + {file = "lxml-4.9.3-cp39-cp39-win32.whl", hash = "sha256:8df133a2ea5e74eef5e8fc6f19b9e085f758768a16e9877a60aec455ed2609b2"}, + {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, + {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"}, + {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, + {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, + {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, + {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, +] + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["BeautifulSoup4"] +source = ["Cython (>=0.29.35)"] + +[[package]] +name = "markdown" +version = "3.3.4" +description = "Python implementation of Markdown." +optional = false +python-versions = ">=3.6" +files = [ + {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, + {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, +] + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "mysqlclient" +version = "2.2.0" +description = "Python interface to MySQL" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mysqlclient-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:68837b6bb23170acffb43ae411e47533a560b6360c06dac39aa55700972c93b2"}, + {file = "mysqlclient-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5670679ff1be1cc3fef0fa81bf39f0cd70605ba121141050f02743eb878ac114"}, + {file = "mysqlclient-2.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:004fe1d30d2c2ff8072f8ea513bcec235fd9b896f70dad369461d0ad7e570e98"}, + {file = "mysqlclient-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:9c6b142836c7dba4f723bf9c93cc46b6e5081d65b2af807f400dda9eb85a16d0"}, + {file = "mysqlclient-2.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:955dba905a7443ce4788c63fdb9f8d688316260cf60b20ff51ac3b1c77616ede"}, + {file = "mysqlclient-2.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:530ece9995a36cadb6211b9787f0c9e05cdab6702549bdb4236af5e9b535ed6a"}, + {file = "mysqlclient-2.2.0.tar.gz", hash = "sha256:04368445f9c487d8abb7a878e3d23e923e6072c04a6c320f9e0dc8a82efba14e"}, +] + +[[package]] +name = "packaging" +version = "23.1" +description = "Core utilities for Python packages" +optional = false +python-versions = ">=3.7" +files = [ + {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, + {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, +] + +[[package]] +name = "pillow" +version = "8.4.0" +description = "Python Imaging Library (Fork)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "Pillow-8.4.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:81f8d5c81e483a9442d72d182e1fb6dcb9723f289a57e8030811bac9ea3fef8d"}, + {file = "Pillow-8.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3f97cfb1e5a392d75dd8b9fd274d205404729923840ca94ca45a0af57e13dbe6"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb9fc393f3c61f9054e1ed26e6fe912c7321af2f41ff49d3f83d05bacf22cc78"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d82cdb63100ef5eedb8391732375e6d05993b765f72cb34311fab92103314649"}, + {file = "Pillow-8.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62cc1afda735a8d109007164714e73771b499768b9bb5afcbbee9d0ff374b43f"}, + {file = "Pillow-8.4.0-cp310-cp310-win32.whl", hash = "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"}, + {file = "Pillow-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:620582db2a85b2df5f8a82ddeb52116560d7e5e6b055095f04ad828d1b0baa39"}, + {file = "Pillow-8.4.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:1bc723b434fbc4ab50bb68e11e93ce5fb69866ad621e3c2c9bdb0cd70e345f55"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72cbcfd54df6caf85cc35264c77ede902452d6df41166010262374155947460c"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70ad9e5c6cb9b8487280a02c0ad8a51581dcbbe8484ce058477692a27c151c0a"}, + {file = "Pillow-8.4.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25a49dc2e2f74e65efaa32b153527fc5ac98508d502fa46e74fa4fd678ed6645"}, + {file = "Pillow-8.4.0-cp36-cp36m-win32.whl", hash = "sha256:93ce9e955cc95959df98505e4608ad98281fff037350d8c2671c9aa86bcf10a9"}, + {file = "Pillow-8.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2e4440b8f00f504ee4b53fe30f4e381aae30b0568193be305256b1462216feff"}, + {file = "Pillow-8.4.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8c803ac3c28bbc53763e6825746f05cc407b20e4a69d0122e526a582e3b5e153"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c8a17b5d948f4ceeceb66384727dde11b240736fddeda54ca740b9b8b1556b29"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1394a6ad5abc838c5cd8a92c5a07535648cdf6d09e8e2d6df916dfa9ea86ead8"}, + {file = "Pillow-8.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:792e5c12376594bfcb986ebf3855aa4b7c225754e9a9521298e460e92fb4a488"}, + {file = "Pillow-8.4.0-cp37-cp37m-win32.whl", hash = "sha256:d99ec152570e4196772e7a8e4ba5320d2d27bf22fdf11743dd882936ed64305b"}, + {file = "Pillow-8.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:7b7017b61bbcdd7f6363aeceb881e23c46583739cb69a3ab39cb384f6ec82e5b"}, + {file = "Pillow-8.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:d89363f02658e253dbd171f7c3716a5d340a24ee82d38aab9183f7fdf0cdca49"}, + {file = "Pillow-8.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a0956fdc5defc34462bb1c765ee88d933239f9a94bc37d132004775241a7585"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b7bb9de00197fb4261825c15551adf7605cf14a80badf1761d61e59da347779"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72b9e656e340447f827885b8d7a15fc8c4e68d410dc2297ef6787eec0f0ea409"}, + {file = "Pillow-8.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5a4532a12314149d8b4e4ad8ff09dde7427731fcfa5917ff16d0291f13609df"}, + {file = "Pillow-8.4.0-cp38-cp38-win32.whl", hash = "sha256:82aafa8d5eb68c8463b6e9baeb4f19043bb31fefc03eb7b216b51e6a9981ae09"}, + {file = "Pillow-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:066f3999cb3b070a95c3652712cffa1a748cd02d60ad7b4e485c3748a04d9d76"}, + {file = "Pillow-8.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:5503c86916d27c2e101b7f71c2ae2cddba01a2cf55b8395b0255fd33fa4d1f1a"}, + {file = "Pillow-8.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4acc0985ddf39d1bc969a9220b51d94ed51695d455c228d8ac29fcdb25810e6e"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b052a619a8bfcf26bd8b3f48f45283f9e977890263e4571f2393ed8898d331b"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:493cb4e415f44cd601fcec11c99836f707bb714ab03f5ed46ac25713baf0ff20"}, + {file = "Pillow-8.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8831cb7332eda5dc89b21a7bce7ef6ad305548820595033a4b03cf3091235ed"}, + {file = "Pillow-8.4.0-cp39-cp39-win32.whl", hash = "sha256:5e9ac5f66616b87d4da618a20ab0a38324dbe88d8a39b55be8964eb520021e02"}, + {file = "Pillow-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:3eb1ce5f65908556c2d8685a8f0a6e989d887ec4057326f6c22b24e8a172c66b"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:ddc4d832a0f0b4c52fff973a0d44b6c99839a9d016fe4e6a1cb8f3eea96479c2"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a3e5ddc44c14042f0844b8cf7d2cd455f6cc80fd7f5eefbe657292cf601d9ad"}, + {file = "Pillow-8.4.0-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c70e94281588ef053ae8998039610dbd71bc509e4acbc77ab59d7d2937b10698"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:3862b7256046fcd950618ed22d1d60b842e3a40a48236a5498746f21189afbbc"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4901622493f88b1a29bd30ec1a2f683782e57c3c16a2dbc7f2595ba01f639df"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84c471a734240653a0ec91dec0996696eea227eafe72a33bd06c92697728046b"}, + {file = "Pillow-8.4.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:244cf3b97802c34c41905d22810846802a3329ddcb93ccc432870243211c79fc"}, + {file = "Pillow-8.4.0.tar.gz", hash = "sha256:b8e2f83c56e141920c39464b852de3719dfbfb6e3c99a2d8da0edf4fb33176ed"}, +] + +[[package]] +name = "pretalx" +version = "2.3.2" +description = "Conference organisation: CfPs, scheduling, much more" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pretalx-2.3.2-py3-none-any.whl", hash = "sha256:526cfdd1842a8b6aa231b4e70da41b85b0759f9e05da09eb3d42a40f00a5e30a"}, + {file = "pretalx-2.3.2.tar.gz", hash = "sha256:011e3377d8abcb30e50b9bf10852ce6df8f72ef68d73eb35fa0e1c3ee78891d3"}, +] + +[package.dependencies] +beautifulsoup4 = ">=4.10.0,<4.11.0" +bleach = ">=4.1.0,<4.2.0" +celery = ">=4.4.0,<4.5.0" +csscompressor = ">=0.9.0,<0.10.0" +cssutils = ">=2.3.0,<2.4.0" +defusedcsv = ">=1.1.0,<1.2.0" +Django = ">=3.2.0,<3.3.0" +django-bootstrap4 = ">=3.0.0,<3.1.0" +django-compressor = ">=3.1.0,<3.2.0" +django-context-decorator = "*" +django-countries = ">=7.0,<8.0" +django-csp = ">=3.7.0,<3.8.0" +django-filter = "21.1" +django-formset-js-improved = "0.5.0.2" +django-formtools = ">=2.3.0,<2.4.0" +django-hierarkey = ">=1.0.0,<1.1.0" +django-i18nfield = ">=1.9.0,<1.10.0" +django-libsass = ">=0.8,<1.0" +django-scopes = ">=1.2.0,<1.3.0" +djangorestframework = ">=3.13.0,<3.14.0" +inlinestyler = ">=0.2.0,<0.3.0" +libsass = ">=0.21.0,<0.22.0" +Markdown = "3.3.4" +Pillow = ">=8.4.0,<8.5.0" +publicsuffixlist = ">=0.7.0,<0.8.0" +python-dateutil = ">=2.8.0,<2.9.0" +pytz = "*" +qrcode = ">=7.0,<8.0" +reportlab = ">=3.5.0,<3.6.0" +requests = ">=2.26.0,<2.27.0" +rules = ">=3.1.0,<3.2.0" +urlman = ">=2.0.1,<2.1.0" +vobject = ">=0.9.0,<0.10.0" +whitenoise = ">=5.3.0,<5.4.0" +zxcvbn = ">=4.4.0,<4.5.0" + +[package.extras] +dev = ["Faker", "black", "build", "check-manifest", "coverage", "djhtml", "docformatter", "flake8 (<4)", "freezegun", "isort", "lxml", "pytest", "pytest-cov", "pytest-django", "pytest-mock", "pytest-rerunfailures", "pytest-sugar", "pytest-xdist", "pywatchman", "responses", "rich", "urllib3"] +mysql = ["mysqlclient"] +postgres = ["psycopg2-binary"] +redis = ["django-redis (>=5.2.0,<5.3.0)", "redis (>=4.0.0,<4.1.0)"] + +[[package]] +name = "psycopg2-binary" +version = "2.9.7" +description = "psycopg2 - Python-PostgreSQL Database Adapter" +optional = false +python-versions = ">=3.6" +files = [ + {file = "psycopg2-binary-2.9.7.tar.gz", hash = "sha256:1b918f64a51ffe19cd2e230b3240ba481330ce1d4b7875ae67305bd1d37b041c"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ea5f8ee87f1eddc818fc04649d952c526db4426d26bab16efbe5a0c52b27d6ab"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2993ccb2b7e80844d534e55e0f12534c2871952f78e0da33c35e648bf002bbff"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbbc3c5d15ed76b0d9db7753c0db40899136ecfe97d50cbde918f630c5eb857a"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:692df8763b71d42eb8343f54091368f6f6c9cfc56dc391858cdb3c3ef1e3e584"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9dcfd5d37e027ec393a303cc0a216be564b96c80ba532f3d1e0d2b5e5e4b1e6e"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17cc17a70dfb295a240db7f65b6d8153c3d81efb145d76da1e4a096e9c5c0e63"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e5666632ba2b0d9757b38fc17337d84bdf932d38563c5234f5f8c54fd01349c9"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7db7b9b701974c96a88997d458b38ccb110eba8f805d4b4f74944aac48639b42"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c82986635a16fb1fa15cd5436035c88bc65c3d5ced1cfaac7f357ee9e9deddd4"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4fe13712357d802080cfccbf8c6266a3121dc0e27e2144819029095ccf708372"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-win32.whl", hash = "sha256:122641b7fab18ef76b18860dd0c772290566b6fb30cc08e923ad73d17461dc63"}, + {file = "psycopg2_binary-2.9.7-cp310-cp310-win_amd64.whl", hash = "sha256:f8651cf1f144f9ee0fa7d1a1df61a9184ab72962531ca99f077bbdcba3947c58"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4ecc15666f16f97709106d87284c136cdc82647e1c3f8392a672616aed3c7151"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3fbb1184c7e9d28d67671992970718c05af5f77fc88e26fd7136613c4ece1f89"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a7968fd20bd550431837656872c19575b687f3f6f98120046228e451e4064df"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:094af2e77a1976efd4956a031028774b827029729725e136514aae3cdf49b87b"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26484e913d472ecb6b45937ea55ce29c57c662066d222fb0fbdc1fab457f18c5"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f309b77a7c716e6ed9891b9b42953c3ff7d533dc548c1e33fddc73d2f5e21f9"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6d92e139ca388ccfe8c04aacc163756e55ba4c623c6ba13d5d1595ed97523e4b"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:2df562bb2e4e00ee064779902d721223cfa9f8f58e7e52318c97d139cf7f012d"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4eec5d36dbcfc076caab61a2114c12094c0b7027d57e9e4387b634e8ab36fd44"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1011eeb0c51e5b9ea1016f0f45fa23aca63966a4c0afcf0340ccabe85a9f65bd"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-win32.whl", hash = "sha256:ded8e15f7550db9e75c60b3d9fcbc7737fea258a0f10032cdb7edc26c2a671fd"}, + {file = "psycopg2_binary-2.9.7-cp311-cp311-win_amd64.whl", hash = "sha256:8a136c8aaf6615653450817a7abe0fc01e4ea720ae41dfb2823eccae4b9062a3"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2dec5a75a3a5d42b120e88e6ed3e3b37b46459202bb8e36cd67591b6e5feebc1"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc10da7e7df3380426521e8c1ed975d22df678639da2ed0ec3244c3dc2ab54c8"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee919b676da28f78f91b464fb3e12238bd7474483352a59c8a16c39dfc59f0c5"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb1c0e682138f9067a58fc3c9a9bf1c83d8e08cfbee380d858e63196466d5c86"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00d8db270afb76f48a499f7bb8fa70297e66da67288471ca873db88382850bf4"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9b0c2b466b2f4d89ccc33784c4ebb1627989bd84a39b79092e560e937a11d4ac"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:51d1b42d44f4ffb93188f9b39e6d1c82aa758fdb8d9de65e1ddfe7a7d250d7ad"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:11abdbfc6f7f7dea4a524b5f4117369b0d757725798f1593796be6ece20266cb"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f02f4a72cc3ab2565c6d9720f0343cb840fb2dc01a2e9ecb8bc58ccf95dc5c06"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-win32.whl", hash = "sha256:81d5dd2dd9ab78d31a451e357315f201d976c131ca7d43870a0e8063b6b7a1ec"}, + {file = "psycopg2_binary-2.9.7-cp37-cp37m-win_amd64.whl", hash = "sha256:62cb6de84d7767164a87ca97e22e5e0a134856ebcb08f21b621c6125baf61f16"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:59f7e9109a59dfa31efa022e94a244736ae401526682de504e87bd11ce870c22"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:95a7a747bdc3b010bb6a980f053233e7610276d55f3ca506afff4ad7749ab58a"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c721ee464e45ecf609ff8c0a555018764974114f671815a0a7152aedb9f3343"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4f37bbc6588d402980ffbd1f3338c871368fb4b1cfa091debe13c68bb3852b3"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac83ab05e25354dad798401babaa6daa9577462136ba215694865394840e31f8"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:024eaeb2a08c9a65cd5f94b31ace1ee3bb3f978cd4d079406aef85169ba01f08"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1c31c2606ac500dbd26381145684d87730a2fac9a62ebcfbaa2b119f8d6c19f4"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:42a62ef0e5abb55bf6ffb050eb2b0fcd767261fa3faf943a4267539168807522"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7952807f95c8eba6a8ccb14e00bf170bb700cafcec3924d565235dffc7dc4ae8"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e02bc4f2966475a7393bd0f098e1165d470d3fa816264054359ed4f10f6914ea"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-win32.whl", hash = "sha256:fdca0511458d26cf39b827a663d7d87db6f32b93efc22442a742035728603d5f"}, + {file = "psycopg2_binary-2.9.7-cp38-cp38-win_amd64.whl", hash = "sha256:d0b16e5bb0ab78583f0ed7ab16378a0f8a89a27256bb5560402749dbe8a164d7"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6822c9c63308d650db201ba22fe6648bd6786ca6d14fdaf273b17e15608d0852"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f94cb12150d57ea433e3e02aabd072205648e86f1d5a0a692d60242f7809b15"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5ee89587696d808c9a00876065d725d4ae606f5f7853b961cdbc348b0f7c9a1"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad5ec10b53cbb57e9a2e77b67e4e4368df56b54d6b00cc86398578f1c635f329"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:642df77484b2dcaf87d4237792246d8068653f9e0f5c025e2c692fc56b0dda70"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a6a8b575ac45af1eaccbbcdcf710ab984fd50af048fe130672377f78aaff6fc1"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f955aa50d7d5220fcb6e38f69ea126eafecd812d96aeed5d5f3597f33fad43bb"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad26d4eeaa0d722b25814cce97335ecf1b707630258f14ac4d2ed3d1d8415265"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:ced63c054bdaf0298f62681d5dcae3afe60cbae332390bfb1acf0e23dcd25fc8"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2b04da24cbde33292ad34a40db9832a80ad12de26486ffeda883413c9e1b1d5e"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-win32.whl", hash = "sha256:18f12632ab516c47c1ac4841a78fddea6508a8284c7cf0f292cb1a523f2e2379"}, + {file = "psycopg2_binary-2.9.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb3b8d55924a6058a26db69fb1d3e7e32695ff8b491835ba9f479537e14dcf9f"}, +] + +[[package]] +name = "publicsuffixlist" +version = "0.7.14" +description = "publicsuffixlist implement" +optional = false +python-versions = ">=2.6" +files = [ + {file = "publicsuffixlist-0.7.14-py2.py3-none-any.whl", hash = "sha256:cc2b74179184a27d9e23b09913d5035b8b040f9e4bfbe47bacf688c15371487f"}, + {file = "publicsuffixlist-0.7.14.tar.gz", hash = "sha256:3b24ad28a7d96b966187b5cbb9f217b06d07ed294cfbfc2a9ca5afcced70cdd5"}, +] + +[package.extras] +readme = ["pandoc"] +update = ["requests"] + +[[package]] +name = "pypng" +version = "0.20220715.0" +description = "Pure Python library for saving and loading PNG images" +optional = false +python-versions = "*" +files = [ + {file = "pypng-0.20220715.0-py3-none-any.whl", hash = "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c"}, + {file = "pypng-0.20220715.0.tar.gz", hash = "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1"}, +] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] + +[[package]] +name = "qrcode" +version = "7.4.2" +description = "QR Code image generator" +optional = false +python-versions = ">=3.7" +files = [ + {file = "qrcode-7.4.2-py3-none-any.whl", hash = "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a"}, + {file = "qrcode-7.4.2.tar.gz", hash = "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +pypng = "*" +typing-extensions = "*" + +[package.extras] +all = ["pillow (>=9.1.0)", "pytest", "pytest-cov", "tox", "zest.releaser[recommended]"] +dev = ["pytest", "pytest-cov", "tox"] +maintainer = ["zest.releaser[recommended]"] +pil = ["pillow (>=9.1.0)"] +test = ["coverage", "pytest"] + +[[package]] +name = "rcssmin" +version = "1.1.0" +description = "CSS Minifier" +optional = false +python-versions = "*" +files = [ + {file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2211a5c91ea14a5937b57904c9121f8bfef20987825e55368143da7d25446e3b"}, + {file = "rcssmin-1.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:7085d1b51dd2556f3aae03947380f6e9e1da29fb1eeadfa6766b7f105c54c9ff"}, + {file = "rcssmin-1.1.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:1512223b6a687bb747e4e531187bd49a56ed71287e7ead9529cbaa1ca4718a0a"}, + {file = "rcssmin-1.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6158d0d86cd611c5304d738dc3d6cfeb23864dd78ad0d83a633f443696ac5d77"}, + {file = "rcssmin-1.1.0-cp310-cp310-manylinux1_i686.whl", hash = "sha256:0a6aae7e119509445bf7aa6da6ca0f285cc198273c20f470ad999ff83bbadcf9"}, + {file = "rcssmin-1.1.0-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:506e33ab4c47051f7deae35b6d8dbb4a5c025f016e90a830929a1ecc7daa1682"}, + {file = "rcssmin-1.1.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:352dd3a78eb914bb1cb269ac2b66b3154f2490a52ab605558c681de3fb5194d2"}, + {file = "rcssmin-1.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:30f5522285065cae0164d20068377d84b5d10b414156115f8729b034d0ea5e8b"}, + {file = "rcssmin-1.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:49807735f26f59404194f1e6f93254b6d5b6f7748c2a954f4470a86a40ff4c13"}, + {file = "rcssmin-1.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f1a37bbd36b050813673e62ae6464467548628690bf4d48a938170e121e8616e"}, + {file = "rcssmin-1.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ddff3a41611664c7f1d9e3d8a9c1669e0e155ac0458e586ffa834dc5953e7d9f"}, + {file = "rcssmin-1.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8b659a88850e772c84cfac4520ec223de6807875e173d8ef3248ab7f90876066"}, + {file = "rcssmin-1.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:1d7c2719d014e4e4df4e33b75ae8067c7e246cf470eaec8585e06e2efac7586c"}, + {file = "rcssmin-1.1.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:37f1242e34ca273ed2c26cf778854e18dd11b31c6bfca60e23fce146c84667c1"}, + {file = "rcssmin-1.1.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f31c82d06ba2dbf33c20db9550157e80bb0c4cbd24575c098f0831d1d2e3c5df"}, + {file = "rcssmin-1.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7da63fee37edf204bbd86785edb4d7491642adbfd1d36fd230b7ccbbd8db1a6f"}, + {file = "rcssmin-1.1.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c28b9eb20982b45ebe6adef8bd2547e5ed314dafddfff4eba806b0f8c166cfd1"}, + {file = "rcssmin-1.1.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:32ccaebbbd4d56eab08cf26aed36f5d33389b9d1d3ca1fecf53eb6ab77760ddf"}, + {file = "rcssmin-1.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:7c44002b79f3656348196005b9522ec5e04f182b466f66d72b16be0bd03c13d8"}, + {file = "rcssmin-1.1.0.tar.gz", hash = "sha256:27fc400627fd3d328b7fe95af2a01f5d0af6b5af39731af5d071826a1f08e362"}, +] + +[[package]] +name = "redis" +version = "5.0.0" +description = "Python client for Redis database and key-value store" +optional = false +python-versions = ">=3.7" +files = [ + {file = "redis-5.0.0-py3-none-any.whl", hash = "sha256:06570d0b2d84d46c21defc550afbaada381af82f5b83e5b3777600e05d8e2ed0"}, + {file = "redis-5.0.0.tar.gz", hash = "sha256:5cea6c0d335c9a7332a460ed8729ceabb4d0c489c7285b0a86dbbf8a017bd120"}, +] + +[package.dependencies] +async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2\""} + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] +ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] + +[[package]] +name = "reportlab" +version = "3.5.68" +description = "The Reportlab Toolkit" +optional = false +python-versions = ">=2.7, >=3.6, <4" +files = [ + {file = "reportlab-3.5.68-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:c0612d9101f40679245e7d9edb169d8d79378a47f38cd8e6b38c55d7ff31db3f"}, + {file = "reportlab-3.5.68-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:19708801278f600d712c04ee6bfb650e45d1b2898713f7bd97b39ab89bd08c1e"}, + {file = "reportlab-3.5.68-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:46f15f5a34a50375c332ab8eaa907a0212c88787b0885ac25a9505c0741ee9ba"}, + {file = "reportlab-3.5.68-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28c72d27f21d74a7301789c7950b5e82a430ed38817ecee060fa1f2f3e959360"}, + {file = "reportlab-3.5.68-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:81d1958d90fccf86f62b38ecbedf9208a973d99e0747b6cd75036914ae8641c4"}, + {file = "reportlab-3.5.68-cp36-cp36m-win32.whl", hash = "sha256:7e466276f1a1121dac23b703af6c22db0cedf6cec5139969f8387e8d8046f203"}, + {file = "reportlab-3.5.68-cp36-cp36m-win_amd64.whl", hash = "sha256:a48221d4ab7de37975ad052f7e565cf13ab708def63f203a38ae9927ab5442cd"}, + {file = "reportlab-3.5.68-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:ced16daf89f948eeb4e376b5d814da5d99f7205fbd42e17a96f257e35dc31bdd"}, + {file = "reportlab-3.5.68-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:70e7461aa47eff810be8c4e4a0cbc6fcf47aecaddd46de6ca4524c76065f8490"}, + {file = "reportlab-3.5.68-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:332f836ff4c975c92d307302e86a54d6f0e3d2ce33a35759812e7a1d17e2091f"}, + {file = "reportlab-3.5.68-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:010f86a192c397f7c8ae667953a85d913395a8a6a8da112bff1c1ea28e679bcd"}, + {file = "reportlab-3.5.68-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6f905390f5e5801b21b6027c8ffaed915e5eec1e46bbdf6a74c8838213717b44"}, + {file = "reportlab-3.5.68-cp37-cp37m-win32.whl", hash = "sha256:63578cab96fc4383e71dd9fe1877bb26ab78b2a6c91139068e99d130687289ab"}, + {file = "reportlab-3.5.68-cp37-cp37m-win_amd64.whl", hash = "sha256:45113c1c359ba314499032c891487802cccd7c4225a3e930d6cf492d62ea4f07"}, + {file = "reportlab-3.5.68-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:b9ae0c534c09274b80f8fd87408071c1f814d56c5f51fe450b2157f1f13e921b"}, + {file = "reportlab-3.5.68-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:66b5a08cbeb910edee7201efa786bd1bf7027c7ec526dddf7d60fc2252e2b30f"}, + {file = "reportlab-3.5.68-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:08b53568979228b6969b790339d06a0b8db8883f92ae7339013f9878042dd9ca"}, + {file = "reportlab-3.5.68-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b57ebeb28f7a58a9da6f8c293acb6d31d89f634b3eba0b728a040cef08afc4ea"}, + {file = "reportlab-3.5.68-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dd3409ebabe699c98058690b7b730f93e6b0bd4ed5e49ca3b15e1530ae07b40b"}, + {file = "reportlab-3.5.68-cp38-cp38-win32.whl", hash = "sha256:2dc5ee0c5b659697cdfbc218ec9abea54dd9c5a95ea8ca95245fe94f5ef111f9"}, + {file = "reportlab-3.5.68-cp38-cp38-win_amd64.whl", hash = "sha256:b25608059558910585a9e229bae0fd3d67af49ae5e1c7a20057680c6b3d5f6f7"}, + {file = "reportlab-3.5.68-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:ad9a49890de59e8dd16fa0ce03ef607e46a5ff2f39de44f8556f796b3d4ddffb"}, + {file = "reportlab-3.5.68-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6063466779e438375bcdd2c15fc551ebd68f16ebfb2766497234df9cfa57e5b1"}, + {file = "reportlab-3.5.68-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5865c4247229584408515055b5b19c7f935ae94433d6258c7a9234c4a07d6d34"}, + {file = "reportlab-3.5.68-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2c0c88a7cf83a20a2bb355f97a1a9d0373a6de60c3aec35d301d3cc75dc4bb72"}, + {file = "reportlab-3.5.68-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6b448a1824d381d282c5ea1da1669a5fa53dac67c57a1ecad6bcc149f286d1fd"}, + {file = "reportlab-3.5.68-cp39-cp39-win32.whl", hash = "sha256:9a00feb8eafbce1283cd3edbb29735bd40c9566b3f45913110a301700c16b63a"}, + {file = "reportlab-3.5.68-cp39-cp39-win_amd64.whl", hash = "sha256:580eed6d9e5c20870ea909bec6840f9ceb9d13c33316d448cae21eb3ca47c7fd"}, + {file = "reportlab-3.5.68.tar.gz", hash = "sha256:efef6a97e3ab49f3f40037dbf9a4166668a17cc6aaba13d5ecbabdf854a9b332"}, +] + +[package.dependencies] +pillow = ">=4.0.0" + +[package.extras] +rlpycairo = ["rlPyCairo (>=0.0.5)"] + +[[package]] +name = "requests" +version = "2.26.0" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, + {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "rjsmin" +version = "1.2.0" +description = "Javascript Minifier" +optional = false +python-versions = "*" +files = [ + {file = "rjsmin-1.2.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e18fe1a610fb105273bb369f61c2b0bd9e66a3f0792e27e4cac44e42ace1968b"}, + {file = "rjsmin-1.2.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:6c395ffc130332cca744f081ed5efd5699038dcb7a5d30c3ff4bc6adb5b30a62"}, + {file = "rjsmin-1.2.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:3b14f4c2933ec194eb816b71a0854ce461b6419a3d852bf360344731ab28c0a6"}, + {file = "rjsmin-1.2.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:54fc30519365841b27556ccc1cb94c5b4413c384ff6d467442fddba66e2e325a"}, + {file = "rjsmin-1.2.0-cp310-cp310-manylinux1_i686.whl", hash = "sha256:40e7211a25d9a11ac9ff50446e41268c978555676828af86fa1866615823bfff"}, + {file = "rjsmin-1.2.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:99e5597a812b60058baa1457387dc79cca7d273b2a700dc98bfd20d43d60711d"}, + {file = "rjsmin-1.2.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:993935654c1311280e69665367d7e6ff694ac9e1609168cf51cae8c0307df0db"}, + {file = "rjsmin-1.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c81229ffe5b0a0d5b3b5d5e6d0431f182572de9e9a077e85dbae5757db0ab75c"}, + {file = "rjsmin-1.2.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:1c93b29fd725e61718299ffe57de93ff32d71b313eaabbfcc7bd32ddb82831d5"}, + {file = "rjsmin-1.2.0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:38a4474ed52e1575fb9da983ec8657faecd8ab3738508d36e04f87769411fd3d"}, + {file = "rjsmin-1.2.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1622fbb6c6a8daaf77da13cc83356539bfe79c1440f9664b02c7f7b150b9a18e"}, + {file = "rjsmin-1.2.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:4387a00777faddf853eebdece9f2e56ebaf243c3f24676a9de6a20c5d4f3d731"}, + {file = "rjsmin-1.2.0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:86c4da7285ddafe6888cb262da563570f28e4a31146b5164a7a6947b1222196b"}, + {file = "rjsmin-1.2.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:d63e193a2f932a786ae82068aa76d1d126fcdff8582094caff9e5e66c4dcc124"}, + {file = "rjsmin-1.2.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:05efa485dfddb6418e3b86d8862463aa15641a61f6ae05e7e6de8f116ee77c69"}, + {file = "rjsmin-1.2.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:b6a7c8c8d19e154334f640954e43e57283e87bb4a2f6e23295db14eea8e9fc1d"}, + {file = "rjsmin-1.2.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2ed83aca637186bafdc894b4b7fc3657e2d74014ccca7d3d69122c1e82675216"}, + {file = "rjsmin-1.2.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:41c7c3910f7b8816e37366b293e576ddecf696c5f2197d53cf2c1526ac336646"}, + {file = "rjsmin-1.2.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:8944a8a55ac825b8e5ec29f341ecb7574697691ef416506885898d2f780fb4ca"}, + {file = "rjsmin-1.2.0.tar.gz", hash = "sha256:6c529feb6c400984452494c52dd9fdf59185afeacca2afc5174a28ab37751a1b"}, +] + +[[package]] +name = "rules" +version = "3.1" +description = "Awesome Django authorization, without the database" +optional = false +python-versions = "*" +files = [ + {file = "rules-3.1-py2.py3-none-any.whl", hash = "sha256:b5e7d7b83dfab8895e901406f533325eccd96b2a38cc0362860c9b895aaad32f"}, + {file = "rules-3.1.tar.gz", hash = "sha256:3293ef232d8ed86ec59dd1d993a745168ebee908a1f0ffb332ed0f3065b8ee57"}, +] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + +[[package]] +name = "soupsieve" +version = "2.4.1" +description = "A modern CSS selector implementation for Beautiful Soup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, + {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, +] + +[[package]] +name = "sqlparse" +version = "0.4.4" +description = "A non-validating SQL parser." +optional = false +python-versions = ">=3.5" +files = [ + {file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"}, + {file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"}, +] + +[package.extras] +dev = ["build", "flake8"] +doc = ["sphinx"] +test = ["pytest", "pytest-cov"] + +[[package]] +name = "typing-extensions" +version = "4.7.1" +description = "Backported and Experimental Type Hints for Python 3.7+" +optional = false +python-versions = ">=3.7" +files = [ + {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, + {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, +] + +[[package]] +name = "urllib3" +version = "1.26.16" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "urllib3-1.26.16-py2.py3-none-any.whl", hash = "sha256:8d36afa7616d8ab714608411b4a3b13e58f463aee519024578e062e141dce20f"}, + {file = "urllib3-1.26.16.tar.gz", hash = "sha256:8f135f6502756bde6b2a9b28989df5fbe87c9970cecaa69041edcce7f0589b14"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] +secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "urlman" +version = "2.0.1" +description = "Django URL pattern helpers" +optional = false +python-versions = "*" +files = [ + {file = "urlman-2.0.1-py2.py3-none-any.whl", hash = "sha256:909177485ddbbeada15a21f80155762faf70fc53be31890ff7288e21803f06c7"}, + {file = "urlman-2.0.1.tar.gz", hash = "sha256:3b9c5ac4e447b1e29fa259dc76953d46d711c84b296a0c66c34870e248eb1205"}, +] + +[[package]] +name = "vine" +version = "1.3.0" +description = "Promises, promises, promises." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "vine-1.3.0-py2.py3-none-any.whl", hash = "sha256:ea4947cc56d1fd6f2095c8d543ee25dad966f78692528e68b4fada11ba3f98af"}, + {file = "vine-1.3.0.tar.gz", hash = "sha256:133ee6d7a9016f177ddeaf191c1f58421a1dcc6ee9a42c58b34bed40e1d2cd87"}, +] + +[[package]] +name = "vobject" +version = "0.9.6.1" +description = "A full-featured Python package for parsing and creating iCalendar and vCard files" +optional = false +python-versions = "*" +files = [ + {file = "vobject-0.9.6.1.tar.gz", hash = "sha256:96512aec74b90abb71f6b53898dd7fe47300cc940104c4f79148f0671f790101"}, +] + +[package.dependencies] +python-dateutil = ">=2.4.0" + +[[package]] +name = "webencodings" +version = "0.5.1" +description = "Character encoding aliases for legacy web content" +optional = false +python-versions = "*" +files = [ + {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, + {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, +] + +[[package]] +name = "whitenoise" +version = "5.3.0" +description = "Radically simplified static file serving for WSGI applications" +optional = false +python-versions = ">=3.5, <4" +files = [ + {file = "whitenoise-5.3.0-py2.py3-none-any.whl", hash = "sha256:d963ef25639d1417e8a247be36e6aedd8c7c6f0a08adcb5a89146980a96b577c"}, + {file = "whitenoise-5.3.0.tar.gz", hash = "sha256:d234b871b52271ae7ed6d9da47ffe857c76568f11dd30e28e18c5869dbd11e12"}, +] + +[package.extras] +brotli = ["Brotli"] + +[[package]] +name = "zxcvbn" +version = "4.4.28" +description = "" +optional = false +python-versions = "*" +files = [ + {file = "zxcvbn-4.4.28.tar.gz", hash = "sha256:151bd816817e645e9064c354b13544f85137ea3320ca3be1fb6873ea75ef7dc1"}, +] + +[metadata] +lock-version = "2.0" +python-versions = ">=3.9,<3.12" +content-hash = "657f172c096d41337c0903619d7a6ddc1062d9bb2a2f3750018d1a9eece5e2d6" diff --git a/pkgs/pretalx/pretalx_service.py b/pkgs/pretalx/pretalx_service.py new file mode 100644 index 00000000..d965ceee --- /dev/null +++ b/pkgs/pretalx/pretalx_service.py @@ -0,0 +1,10 @@ +import sys +import os + + +module_name = 'pretalx' + + +def main(): + os.environ['PYTHONPATH'] = ':'.join(sys.path) + os.execv(sys.executable, [sys.executable, '-m', module_name] + sys.argv[1:]) diff --git a/pkgs/pretalx/pyproject.toml b/pkgs/pretalx/pyproject.toml new file mode 100644 index 00000000..1af56232 --- /dev/null +++ b/pkgs/pretalx/pyproject.toml @@ -0,0 +1,41 @@ +[tool.poetry] +name = "pretalx-service" +version = "0.1.0" +description = "Pretalx service" +authors = [] # see meta.maintainers in pkgs/pretalx/default.nix + +[tool.poetry.dependencies] +python = ">=3.9,<3.12" +pretalx = "2.3.2" + +[tool.poetry.group.mysql] +optional = true + +[tool.poetry.group.mysql.dependencies] +mysqlclient = "*" + +[tool.poetry.group.postgresql] +optional = true + +[tool.poetry.group.postgresql.dependencies] +psycopg2-binary = "*" + +[tool.poetry.group.redis] +optional = true + +[tool.poetry.group.redis.dependencies] +django-redis = "*" + +[tool.poetry.group.test] +optional = true + +[tool.poetry.group.test.dependencies] +Faker = "*" +freezegun = "*" + +[tool.poetry.scripts] +pretalx = "pretalx_service:main" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/all-tests.nix b/tests/all-tests.nix new file mode 100644 index 00000000..4275d6b3 --- /dev/null +++ b/tests/all-tests.nix @@ -0,0 +1,15 @@ +{ + configurations, + modules, + ... +}: { + pretalx = import ./pretalx { + configurations.server = {...}: { + imports = [ + modules.pretalx + modules.sops-nix + configurations.pretalx-postgresql + ]; + }; + }; +} diff --git a/tests/pretalx/default.nix b/tests/pretalx/default.nix new file mode 100644 index 00000000..05317b6d --- /dev/null +++ b/tests/pretalx/default.nix @@ -0,0 +1,51 @@ +{configurations, ...}: { + name = "pretalx tests"; + + nodes = { + server = {pkgs, ...}: { + imports = [ + configurations.server + ]; + + sops = pkgs.lib.mkForce { + age.keyFile = ./sops/keys.txt; + defaultSopsFile = ./sops/pretalx.yaml; + }; + + # Use kmscon + # to provide a slightly nicer console, and while we're at it, + # also use a nice font. + # With kmscon, we can for example zoom in/out using [Ctrl] + [+] + # and [Ctrl] + [-] + services.kmscon = { + enable = true; + fonts = [ + { + name = "Fira Code"; + package = pkgs.fira-code; + } + ]; + }; + }; + }; + + testScript = {nodes, ...}: '' + start_all() + + with subtest("pretalx-web"): + # NOTE: We cannot use just `server.wait_for_unit("pretalx-web.service")` + # because the systemd service will change state to "active", before + # pretalx is actually ready to serve requests, leading to failure. + # pretalx/Django does not support the sd_notify protocol as of + # 2023-08-11, which could be used to notify systemd about the state of + # the webserver. + server.wait_for_unit("pretalx-web.service") + server.wait_until_succeeds("curl --fail --connect-timeout 2 localhost", timeout=60) + + server.execute("pretalx create_test_event") + + # NOTE: "democon" is the slug of the event created by + # `pretalx create_test_event`. + server.succeed("curl --fail --connect-timeout 10 http://localhost/democon") + ''; +} diff --git a/tests/pretalx/sops/.sops.yaml b/tests/pretalx/sops/.sops.yaml new file mode 100644 index 00000000..db59310f --- /dev/null +++ b/tests/pretalx/sops/.sops.yaml @@ -0,0 +1,13 @@ +keys: + - &augustebaum age16xvvc22xm35xzgc2yx6cpcz6wnww8tf6rwfmrag4vnem7nq6h9pqr33epg + - &imincik age1przhvgd5gy3kvcmlmsu47qmauhdwzzez7z89xyv623em72e9mfzskefwcl + - &lorenzleutgeb age1c0g6s6daxy79dlm9uqczwlkh0hvjpghw5h8zzljc3vs275rvvqus30hv9l + - &test age1fdlfm8xh97fwpf8fls9245wre9aktg6dzhhrr02s028kz4g38ajss2guwx +creation_rules: + - path_regex: ^pretalx\.yaml$ + key_groups: + - age: + - *augustebaum + - *imincik + - *lorenzleutgeb + - *test diff --git a/tests/pretalx/sops/keys.txt b/tests/pretalx/sops/keys.txt new file mode 100644 index 00000000..22617a6f --- /dev/null +++ b/tests/pretalx/sops/keys.txt @@ -0,0 +1,2 @@ +# public key: age1fdlfm8xh97fwpf8fls9245wre9aktg6dzhhrr02s028kz4g38ajss2guwx +AGE-SECRET-KEY-1WFRN5AHQSE0XWPF82E2ZX3SJTLUXFWETX0XKY7PMLV36PYNQ6XUQ2AZ6M4 diff --git a/tests/pretalx/sops/pretalx.yaml b/tests/pretalx/sops/pretalx.yaml new file mode 100644 index 00000000..10f09c35 --- /dev/null +++ b/tests/pretalx/sops/pretalx.yaml @@ -0,0 +1,58 @@ +pretalx: + init: + admin: + password: ENC[AES256_GCM,data:HFmCdMCZSeaSojlw8jtz,iv:q7gAG7IQFnTaEVd4RAgKIYNnPTB14SP6Vk2FaGN4y6Y=,tag:QknRTcOPnujhsoPAZ0SDMQ==,type:str] + database: + password: ENC[AES256_GCM,data:jvEADqgcAw==,iv:FDgqeyMw1f8q+fQEOZdfZClDZkXGAV8VtET5MycuLyo=,tag:tteachf+Dr6QFILlDApDAg==,type:str] + redis: + location: ENC[AES256_GCM,data:/yU+DeBRprNMSSSvWVSrFucNzlOjalhZffFuXqxNoBTIZ7Bgr179vRI=,iv:I2B3aQdchi+gR36HMw44Wwhq162gqnr/mE/bvD/Jkas=,tag:Ev0lc5EC3qW2slKGqGeTEA==,type:str] + celery: + backend: ENC[AES256_GCM,data:Iuue4IeJaimrllLM3u+5fQl8xRkSvuE904h9HhoYNlulrbQBNohUlJ/sulTTdctB6ng78ZhMgJ0lr+0=,iv:W2GRD12jqechJEBrb4DAc/5qJE9pnOU/IJZRenZhE64=,tag:jkh5L3LMgg2+8SfapQXS7w==,type:str] + broker: ENC[AES256_GCM,data:2ivljYXHpnuUdz3eYgmuVUtkRY39e/41o3TwUHiit1dZ8Bcgr2YDrWBzOosa0mwXlwDHvMck/uGXhoQ=,iv:IxBzAWvvlJqaW9uW5IgtFxL6Xxe21ReQeZulhuHmKBM=,tag:iKtl9Zbi1qNJ6kq2II6Z1w==,type:str] +sops: + kms: [] + gcp_kms: [] + azure_kv: [] + hc_vault: [] + age: + - recipient: age16xvvc22xm35xzgc2yx6cpcz6wnww8tf6rwfmrag4vnem7nq6h9pqr33epg + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0MlArR3YvbHQyYjZ0T056 + TUoveS94RUlGenpSRzlYMjBZTkNpSlZVNzJvCjdOY3B3Rnd4aDZnRzl0b2IzdzZR + Z1RCUEtCUkN4cVM0UzBsNTBrU1hLVnMKLS0tIElsRS9Nc3Q4RDgzcFh6YWtqdlg1 + SmVqWGRhNlpQUWhJTGJGUkZtWG1tSEEKjr1g2mZkm5dhNe+XIVKz5CN4w+X43GiZ + SePZ/bDipPa8F4FVlDXU9zx/wa2bi1AWKd1qT0hYIojYop0w9eEV6Q== + -----END AGE ENCRYPTED FILE----- + - recipient: age1przhvgd5gy3kvcmlmsu47qmauhdwzzez7z89xyv623em72e9mfzskefwcl + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBTbVRHSFkwZlFBUDFweVI0 + TXpBV3lad1pLbStQMUNTek8rQW96djlFWTFZClFheTdTT21nOVFQMm9ONEFOTUxu + TkhyRnhzcnV6TTFneVdhSFVVZW03UHMKLS0tIDduOENGREJtaDI4MjN3Zkt0TTdI + WDRrMHFqWkF3OGFEQlEzc1JPQkhjVTgKTxS9q4EME1jD0ZC4aqFsuLON0JKCN9hP + MklnS/abIIrHG0ukFCu6TkDFLXKRnyqNcQ9DkrAmKIqydcw3m4e6Ug== + -----END AGE ENCRYPTED FILE----- + - recipient: age1c0g6s6daxy79dlm9uqczwlkh0hvjpghw5h8zzljc3vs275rvvqus30hv9l + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBjR3RCbkt0VWpHMHE0Y2xW + UmlIM2dVVE1BcERZYTlVQjdvRTArdWR4Mkc0CkNza0k3aHRRUzdVR3NUVmJ5TkFm + cTg2ZVZ1MXRBaGh6Qmp6Y0grUnBuR00KLS0tIFRXd00xR1hrS1hWQkM2dXpLUE55 + VDJsNkF0MHlVei9xK2E3eEUvNXJINHMKvZ1/cVjRtjVxvPmQIKWSAenOiOwDAvpj + xEbAz6RkwILB6Z7oM9y0EfcrlAa/2xjk3M5mgvTldovmJB8KN3SV/A== + -----END AGE ENCRYPTED FILE----- + - recipient: age1fdlfm8xh97fwpf8fls9245wre9aktg6dzhhrr02s028kz4g38ajss2guwx + enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBaOUxNQUpCbENDczJPUlhH + L04zdk1PbFVnRTdkWUVYMEZpOWM2S29IdWhnCmdEZmNFc3B1S2YxWS9VZU9xT25w + ZWdSZmdtTCtzQStWdlp6SzRwelplTmMKLS0tIENscTJ5V0QzbE9Ca1FMN2FtdU1W + dmNaMVQ5alo1dkFRa1lvbVlockYrNlUKTQ8LzlEwEzm/7hNdMLidmQyR4ddyleS2 + ANoywmxn2jZaeAf/RAqaMiJmr/ak5KrXOw+paptcuMEtnFBfEwS4UA== + -----END AGE ENCRYPTED FILE----- + lastmodified: "2023-08-17T08:14:09Z" + mac: ENC[AES256_GCM,data:5nnbCl49E6k8cuORVIwsXVx62xLtMo2li3D78HPjJJWK19CH2uFFChLFDyejanhPbUvBHGjJ5M32wvpCKmP+ar3IM4cpNMBf1dkSz98f3sm4iLoICQFL60BOJz+N+cFeBpYQiewXoe/DNzaSKd7U6O/PFUsJzgY41bpMyMPyOfg=,iv:B62Niq5rz/3rHCIBYcjOVqEPEhJ0BoE+BbKaJqnsWiM=,tag:+AzZJe1tPlHB09tCbC1tPA==,type:str] + pgp: [] + unencrypted_suffix: _unencrypted + version: 3.7.3