3 Commits

Author SHA1 Message Date
91f924a75a devenv 2024-11-15 02:34:52 +01:00
e1605aab29 modernize 2024-11-15 00:26:50 +01:00
1164d492a3 conf.py does nothing 2024-11-15 00:18:26 +01:00
16 changed files with 442 additions and 176 deletions

3
.envrc

@ -1 +1,2 @@
use flake
# devenv needs to know the path to the current working directory to create and manage mutable state
use flake . --no-pure-eval

6
.gitignore vendored

@ -1,8 +1,12 @@
result
result-*
.venv
.direnv
.devenv
dist
config.ini
test.db
.ruff_cache
.ruff_cache

@ -2,48 +2,47 @@
EDB-system for PVVVV
## Hva er dette?
Dibbler er et system laget av PVVere for PVVere for å byttelåne både matvarer og godis.
Det er designet for en gammeldags VT terminal, og er laget for å være enkelt både å bruke og å hacke på.
Programmet er skrevet i Python, og bruker en sql database for å lagre data.
Samlespleiseboden er satt opp slik at folk kjøper inn varer, og får dibblerkreditt, og så kan man bruke
denne kreditten til å kjøpe ut andre varer. Det er ikke noen form for authentisering, så hele systemet er basert på tillit.
Det er anbefalt å koble en barkodeleser til systemet for å gjøre det enklere å både legge til og kjøpe varer.
## Kom i gang
Installer python, og lag og aktiver et venv. Installer så avhengighetene med `pip install`.
Deretter kan du kjøre programmet med
```console
python -m dibbler -c example-config.ini create-db
python -m dibbler -c example-config.ini loop
```
## Nix
### Bygge nytt image
### Hvordan kjøre
nix run github:Programvareverkstedet/dibbler
### Hvordan utvikle?
python -m venv .venv
source .venv/activate
pip install -e .
cp example-config.ini config.ini
dibbler -c config.ini create-db
dibbler -c config.ini loop
eller hvis du tolererer nix og postgres:
direnv allow # eller bare `nix develop`
devenv up
dibbler create-db
dibbler loop
### Bygge image
For å bygge et image trenger du en builder som takler å bygge for arkitekturen du skal lage et image for.
(Eller be til gudene om at cross compile funker)
_(Eller be til gudene om at cross compile funker)_
Flaket exposer en modul som autologger inn med en bruker som automatisk kjører dibbler, og setter opp et minimalistisk miljø.
Før du bygger imaget burde du kopiere og endre `example-config.ini` lokalt til å inneholde instillingene dine. **NB: Denne kommer til å ligge i nix storen, ikke si noe her som du ikke vil at moren din skal høre.**
Før du bygger imaget burde du lage en `config.ini` fil lokalt som inneholder instillingene dine. **NB: Denne kommer til å ligge i nix storen.**
Du kan også endre hvilken config-fil som blir brukt direkte i pakken eller i modulen.
Du kan også endre hvilken `config.ini` som blir brukt direkte i pakken eller i modulen.
Se eksempelet for hvordan skrot er satt opp i `flake.nix` og `nix/skrott.nix`
Se eksempelet for hvordan skrot er satt opp i `flake.nix`
### Bygge image for skrot
Skrot har et image definert i flake.nix:
1. endre `example-config.ini`
Skrot har et system image definert i `flake.nix`:
1. lag `config.ini` (`cp {example-,}config.ini`)
2. `nix build .#images.skrot`
3. ???
4. non-profit
4. non-profit!

4
default.nix Normal file

@ -0,0 +1,4 @@
{ pkgs ? import <nixos-unstable> { } }:
{
dibbler = pkgs.callPackage ./nix/dibbler.nix { };
}

@ -1,16 +1,12 @@
from pathlib import Path
import os
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from dibbler.conf import config
if (url := config.get("database", "url")) is not None:
database_url = url
elif (url_file := config.get("database", "url_file")) is not None:
with Path(url_file).open() as file:
database_url = file.read().strip()
engine = create_engine(database_url)
engine = create_engine(
os.environ.get("DIBBLER_DATABASE_URL")
or config.get("database", "url")
)
Session = sessionmaker(bind=engine)

@ -2,7 +2,7 @@ import os
from PIL import ImageFont
from barcode.writer import ImageWriter, mm2px
from brother_ql.labels import ALL_LABELS
from brother_ql.devicedependent import label_type_specs
def px2mm(px, dpi=300):
@ -12,15 +12,14 @@ def px2mm(px, dpi=300):
class BrotherLabelWriter(ImageWriter):
def __init__(self, typ="62", max_height=350, rot=False, text=None):
super(BrotherLabelWriter, self).__init__()
label = next([l for l in ALL_LABELS if l.identifier == typ])
assert label is not None
assert typ in label_type_specs
self.rot = rot
if self.rot:
self._h, self._w = label.dots_printable
self._h, self._w = label_type_specs[typ]["dots_printable"]
if self._w == 0 or self._w > max_height:
self._w = min(max_height, self._h / 2)
else:
self._w, self._h = label.dots_printable
self._w, self._h = label_type_specs[typ]["dots_printable"]
if self._h == 0 or self._h > max_height:
self._h = min(max_height, self._w / 2)
self._xo = 0.0

@ -2,10 +2,9 @@ import os
import datetime
import barcode
from brother_ql.brother_ql_create import create_label
from brother_ql.raster import BrotherQLRaster
from brother_ql import BrotherQLRaster, create_label
from brother_ql.backends import backend_factory
from brother_ql.labels import ALL_LABELS
from brother_ql.devicedependent import label_type_specs
from PIL import Image, ImageDraw, ImageFont
from .barcode_helpers import BrotherLabelWriter
@ -18,11 +17,10 @@ def print_name_label(
label_type="62",
printer_type="QL-700",
):
label = next([l for l in ALL_LABELS if l.identifier == label_type])
if not rotate:
width, height = label.dots_printable
width, height = label_type_specs[label_type]["dots_printable"]
else:
height, width = label.dots_printable
height, width = label_type_specs[label_type]["dots_printable"]
font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "ChopinScript.ttf")
fs = 2000

@ -1,4 +1,6 @@
import argparse
import sys
import os
from dibbler.conf import config
@ -10,6 +12,7 @@ parser.add_argument(
help="Path to the config file",
type=str,
required=False,
default=os.environ.get("DIBBLER_CONFIG_FILE", None)
)
subparsers = parser.add_subparsers(
@ -24,6 +27,8 @@ subparsers.add_parser("slabbedasker", help="Find out who is slabbedasker")
def main():
args = parser.parse_args()
if args.config is None:
print("ERROR: no config was provided", file=sys.stderr)
config.read(args.config)
if args.subcommand == "loop":

@ -1,18 +1,20 @@
[general]
quit_allowed = true
stop_allowed = false
; quit_allowed = false
; stop_allowed = false
quit_allowed = true ; not for prod
stop_allowed = true ; not for prod
show_tracebacks = true
input_encoding = 'utf8'
[database]
; url = postgresql://robertem@127.0.0.1/pvvvv
url = sqlite:///test.db
; url = postgresql://dibbler:hunter2@127.0.0.1/pvvvv
url = sqlite:///test.db ; devenv will override this to postgres using DIBBLER_DATABASE_URL
[limits]
low_credit_warning_limit = -100
user_recent_transaction_limit = 100
# See https://pypi.org/project/brother_ql/ for label types
# See https://pypi.org/project/brother_ql_next/ for label types
# Set rotate to False for endless labels
[printer]
label_type = "62"

249
flake.lock generated

@ -1,5 +1,93 @@
{
"nodes": {
"cachix": {
"inputs": {
"devenv": [
"devenv"
],
"flake-compat": [
"devenv"
],
"git-hooks": [
"devenv"
],
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1728672398,
"narHash": "sha256-KxuGSoVUFnQLB2ZcYODW7AVPAh9JqRlD5BrfsC/Q4qs=",
"owner": "cachix",
"repo": "cachix",
"rev": "aac51f698309fd0f381149214b7eee213c66ef0a",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "latest",
"repo": "cachix",
"type": "github"
}
},
"devenv": {
"inputs": {
"cachix": "cachix",
"flake-compat": "flake-compat",
"git-hooks": "git-hooks",
"nix": "nix",
"nixpkgs": "nixpkgs_3"
},
"locked": {
"lastModified": 1731619804,
"narHash": "sha256-wyxFaVooL8SzvQNpolpx32X+GoBPnCAg9E0i/Ekn3FU=",
"owner": "cachix",
"repo": "devenv",
"rev": "87edaaf1dddf17fe16eabab3c8edaf7cca2c3bc2",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"devenv",
"nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1712014858,
"narHash": "sha256-sB4SWl2lX95bExY2gMFG5HIzvva5AVMJd4Igm+GpZNw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "9126214d0a59633752a136528f5f3b9aa8565b7d",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-utils": {
"inputs": {
"systems": "systems"
@ -13,17 +101,117 @@
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"git-hooks": {
"inputs": {
"flake-compat": [
"devenv"
],
"gitignore": "gitignore",
"nixpkgs": [
"devenv",
"nixpkgs"
],
"nixpkgs-stable": [
"devenv"
]
},
"locked": {
"lastModified": 1730302582,
"narHash": "sha256-W1MIJpADXQCgosJZT8qBYLRuZls2KSiKdpnTVdKBuvU=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "af8a16fe5c264f5e9e18bcee2859b40a656876cf",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"devenv",
"git-hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"libgit2": {
"flake": false,
"locked": {
"lastModified": 1697646580,
"narHash": "sha256-oX4Z3S9WtJlwvj0uH9HlYcWv+x1hqp8mhXl7HsLu2f0=",
"owner": "libgit2",
"repo": "libgit2",
"rev": "45fd9ed7ae1a9b74b957ef4f337bc3c8b3df01b5",
"type": "github"
},
"original": {
"owner": "libgit2",
"repo": "libgit2",
"type": "github"
}
},
"nix": {
"inputs": {
"flake-compat": [
"devenv"
],
"flake-parts": "flake-parts",
"libgit2": "libgit2",
"nixpkgs": "nixpkgs_2",
"nixpkgs-23-11": [
"devenv"
],
"nixpkgs-regression": [
"devenv"
],
"pre-commit-hooks": [
"devenv"
]
},
"locked": {
"lastModified": 1727438425,
"narHash": "sha256-X8ES7I1cfNhR9oKp06F6ir4Np70WGZU5sfCOuNBEwMg=",
"owner": "domenkozar",
"repo": "nix",
"rev": "f6c5ae4c1b2e411e6b1e6a8181cc84363d6a7546",
"type": "github"
},
"original": {
"owner": "domenkozar",
"ref": "devenv-2.24",
"repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1742288794,
"narHash": "sha256-Txwa5uO+qpQXrNG4eumPSD+hHzzYi/CdaM80M9XRLCo=",
"lastModified": 1730531603,
"narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b6eaf97c6960d97350c584de1b6dcff03c9daf42",
"rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github"
},
"original": {
@ -33,10 +221,59 @@
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1717432640,
"narHash": "sha256-+f9c4/ZX5MWDOuB1rKoWj+lBNm0z0rs4CK47HBLxy1o=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "88269ab3044128b7c2f4c7d68448b2fb50456870",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "release-24.05",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1716977621,
"narHash": "sha256-Q1UQzYcMJH4RscmpTkjlgqQDX5yi1tZL0O345Ri6vXQ=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs_4": {
"locked": {
"lastModified": 1731611831,
"narHash": "sha256-R51rOqkWMfubBkZ9BY4Y1VaRoeqEBshlfQ8mMH5RjqI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "cea28c811faadb50bee00d433bbf2fea845a43e4",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable-small",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
"nixpkgs": "nixpkgs_4"
}
},
"systems": {

155
flake.nix

@ -1,65 +1,128 @@
{
description = "Dibbler samspleisebod";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable-small";
flake-utils.url = "github:numtide/flake-utils";
devenv.url = "github:cachix/devenv";
};
outputs = { self, nixpkgs, flake-utils }: let
inherit (nixpkgs) lib;
systems = [
"x86_64-linux"
"aarch64-linux"
"x86_64-darwin"
"aarch64-darwin"
nixConfig = {
extra-trusted-public-keys = [
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
];
forAllSystems = f: lib.genAttrs systems (system: let
pkgs = nixpkgs.legacyPackages.${system};
in f system pkgs);
in {
packages = forAllSystems (system: pkgs: {
extra-substituters = [
"https://devenv.cachix.org"
];
};
outputs = { self, ... } @ inputs:
inputs.flake-utils.lib.eachDefaultSystem (system: let
pkgs = inputs.nixpkgs.legacyPackages.${system};
inherit (pkgs) lib;
in {
packages = {
default = self.packages.${system}.dibbler;
dibbler = pkgs.callPackage ./nix/dibbler.nix {
python3Packages = pkgs.python312Packages;
};
skrot = self.nixosConfigurations.skrot.config.system.build.sdImage;
});
apps = forAllSystems (system: pkgs: {
default = self.apps.${system}.dibbler;
dibbler = flake-utils.lib.mkApp {
drv = self.packages.${system}.dibbler;
};
});
dibbler = pkgs.python311Packages.callPackage ./nix/dibbler.nix { };
skrot-vm = self.nixosConfigurations.skrot.config.system.build.vm;
overlays = {
default = self.overlays.dibbler;
dibbler = final: prev: {
inherit (self.packages.${prev.system}) dibbler;
# devenv cruft
devenv-up = self.devShells.${system}.default.config.procfileScript;
devenv-test = self.devShells.${system}.default.config.test;
};
devShells = {
default = self.devShells.${system}.dibbler;
dibbler = inputs.devenv.lib.mkShell {
inherit inputs pkgs;
modules = [({ config, ... }: {
# https://devenv.sh/reference/options/
enterShell = ''
if [[ ! -f config.ini ]]; then
cp -v example-config.ini config.ini
fi
export REPO_ROOT=$(realpath .) # used by mkPythonEditablePackage
export DIBBLER_CONFIG_FILE=$(realpath config.ini)
export DIBBLER_DATABASE_URL=postgresql://dibbler:hunter2@/dibbler?host=${config.env.PGHOST}
'';
packages = [
/* self.packages.${system}.dibbler */
(pkgs.python311Packages.mkPythonEditablePackage {
inherit (self.packages.${system}.dibbler)
pname version
build-system dependencies;
scripts = (lib.importTOML ./pyproject.toml).project.scripts;
root = "$REPO_ROOT";
})
pkgs.python311Packages.black
pkgs.ruff
];
services.postgres = {
enable = true;
initialDatabases = [
{
name = "dibbler";
user = "dibbler";
pass = "hunter2";
}
];
};
})];
};
};
devShells = forAllSystems (system: pkgs: {
default = self.devShells.${system}.dibbler;
dibbler = pkgs.callPackage ./nix/shell.nix {
python = pkgs.python312;
};
});
# Note: using the module requires that you have applied the overlay first
})
//
{
# Note: using the module requires that you have applied the
# overlay first
nixosModules.default = import ./nix/module.nix;
nixosConfigurations.skrot = nixpkgs.lib.nixosSystem (rec {
images.skrot = self.nixosConfigurations.skrot.config.system.build.sdImage;
nixosConfigurations.skrot = inputs.nixpkgs.lib.nixosSystem {
system = "aarch64-linux";
pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.dibbler ];
};
modules = [
(nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix")
(inputs.nixpkgs + "/nixos/modules/installer/sd-card/sd-image-aarch64.nix")
self.nixosModules.default
./nix/skrott.nix
({...}: {
system.stateVersion = "22.05";
networking = {
hostName = "skrot";
domain = "pvv.ntnu.no";
nameservers = [ "129.241.0.200" "129.241.0.201" ];
defaultGateway = "129.241.210.129";
interfaces.eth0 = {
useDHCP = false;
ipv4.addresses = [{
address = "129.241.210.235";
prefixLength = 25;
}];
};
};
# services.resolved.enable = true;
# systemd.network.enable = true;
# systemd.network.networks."30-network" = {
# matchConfig.Name = "*";
# DHCP = "no";
# address = [ "129.241.210.235/25" ];
# gateway = [ "129.241.210.129" ];
# };
})
];
});
};
};
}

@ -1,21 +1,25 @@
{ lib
, python3Packages
, fetchFromGitHub
, buildPythonApplication
, setuptools
, brother-ql
, matplotlib
, psycopg2
, python-barcode
, sqlalchemy
}:
python3Packages.buildPythonApplication {
buildPythonApplication {
pname = "dibbler";
version = "unstable";
version = "0.0.0";
pyproject = true;
src = lib.cleanSource ../.;
format = "pyproject";
# brother-ql is breaky breaky
# https://github.com/NixOS/nixpkgs/issues/285234
dontCheckRuntimeDeps = true;
nativeBuildInputs = with python3Packages; [ setuptools ];
propagatedBuildInputs = with python3Packages; [
brother-ql
build-system = [ setuptools ];
dependencies = [
# we override pname to satisfy mkPythonEditablePackage
(brother-ql.overridePythonAttrs { pname = "brother-ql-next"; })
matplotlib
psycopg2
python-barcode

@ -2,20 +2,15 @@
cfg = config.services.dibbler;
in {
options.services.dibbler = {
enable = lib.mkEnableOption "dibbler, the little kiosk computer";
package = lib.mkPackageOption pkgs "dibbler" { };
config = lib.mkOption {
type = lib.types.path;
description = "Path to the configuration file.";
default = ../example-config.ini;
default = ../conf.py;
};
};
config = let
screen = "${pkgs.screen}/bin/screen";
in lib.mkIf cfg.enable {
in {
boot = {
consoleLogLevel = 0;
enableContainers = false;
@ -28,7 +23,10 @@ in {
group = "dibbler";
extraGroups = [ "lp" ];
isNormalUser = true;
shell = ((pkgs.writeShellScriptBin "login-shell" "${screen} -x dibbler") // {shellPath = "/bin/login-shell";});
shell = (
(pkgs.writeShellScriptBin "login-shell" "${screen} -x dibbler")
// {shellPath = "/bin/login-shell";}
);
};
};
@ -74,7 +72,7 @@ in {
console.keyMap = "no";
programs.command-not-found.enable = false;
i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" ];
# environment.noXlibs = true;
environment.noXlibs = true;
documentation = {
info.enable = false;

@ -1,18 +0,0 @@
{
mkShell,
python,
ruff,
}:
mkShell {
packages = [
ruff
(python.withPackages (ps: with ps; [
brother-ql
matplotlib
psycopg2
python-barcode
sqlalchemy
]))
];
}

@ -1,27 +0,0 @@
{...}: {
system.stateVersion = "25.05";
services.dibbler.enable = true;
networking = {
hostName = "skrot";
domain = "pvv.ntnu.no";
nameservers = [ "129.241.0.200" "129.241.0.201" ];
defaultGateway = "129.241.210.129";
interfaces.eth0 = {
useDHCP = false;
ipv4.addresses = [{
address = "129.241.210.235";
prefixLength = 25;
}];
};
};
# services.resolved.enable = true;
# systemd.network.enable = true;
# systemd.network.networks."30-network" = {
# matchConfig.Name = "*";
# DHCP = "no";
# address = [ "129.241.210.235/25" ];
# gateway = [ "129.241.210.129" ];
# };
}

@ -8,12 +8,13 @@ authors = []
description = "EDB-system for PVV"
readme = "README.md"
requires-python = ">=3.11"
license = {text = "BSD-3-Clause"}
classifiers = [
"Programming Language :: Python :: 3",
]
dependencies = [
"SQLAlchemy >= 2.0, <2.1",
"brother-ql",
"brother_ql_next",
"matplotlib",
"psycopg2 >= 2.8, <2.10",
"python-barcode",