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
21 changed files with 480 additions and 331 deletions

3
.envrc
View File

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

7
.gitignore vendored
View File

@@ -1,9 +1,12 @@
result result
result-* result-*
**/__pycache__ .venv
dibbler.egg-info .direnv
.devenv
dist dist
config.ini
test.db test.db
.ruff_cache .ruff_cache

View File

@@ -2,48 +2,47 @@
EDB-system for PVVVV 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 ## 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. 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ø. 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 ### 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` 2. `nix build .#images.skrot`
3. ??? 3. ???
4. non-profit 4. non-profit!

4
default.nix Normal file
View File

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

View File

@@ -1,16 +1,12 @@
from pathlib import Path import os
from sqlalchemy import create_engine from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker from sqlalchemy.orm import sessionmaker
from dibbler.conf import config from dibbler.conf import config
if (url := config.get("database", "url")) is not None: engine = create_engine(
database_url = url os.environ.get("DIBBLER_DATABASE_URL")
or config.get("database", "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)
Session = sessionmaker(bind=engine) Session = sessionmaker(bind=engine)

View File

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

View File

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

View File

@@ -76,8 +76,12 @@ class Database:
personDatoVerdi = defaultdict(list) # dict->array personDatoVerdi = defaultdict(list) # dict->array
personUkedagVerdi = defaultdict(list) personUkedagVerdi = defaultdict(list)
# for global # for global
personPosTransactions = {} # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen personPosTransactions = (
personNegTransactions = {} # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen {}
) # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen
personNegTransactions = (
{}
) # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen
globalVareAntall = {} # globalVareAntall[Oreo] == 3 globalVareAntall = {} # globalVareAntall[Oreo] == 3
globalVareVerdi = {} # globalVareVerdi[Oreo] == 30 #[kr] globalVareVerdi = {} # globalVareVerdi[Oreo] == 30 #[kr]
globalPersonAntall = {} # globalPersonAntall[trygvrad] == 3 globalPersonAntall = {} # globalPersonAntall[trygvrad] == 3

View File

@@ -1,4 +1,6 @@
import argparse import argparse
import sys
import os
from dibbler.conf import config from dibbler.conf import config
@@ -10,6 +12,7 @@ parser.add_argument(
help="Path to the config file", help="Path to the config file",
type=str, type=str,
required=False, required=False,
default=os.environ.get("DIBBLER_CONFIG_FILE", None)
) )
subparsers = parser.add_subparsers( subparsers = parser.add_subparsers(
@@ -20,11 +23,12 @@ subparsers = parser.add_subparsers(
subparsers.add_parser("loop", help="Run the dibbler loop") subparsers.add_parser("loop", help="Run the dibbler loop")
subparsers.add_parser("create-db", help="Create the database") subparsers.add_parser("create-db", help="Create the database")
subparsers.add_parser("slabbedasker", help="Find out who is slabbedasker") subparsers.add_parser("slabbedasker", help="Find out who is slabbedasker")
subparsers.add_parser("seed-data", help="Fill with mock data")
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
if args.config is None:
print("ERROR: no config was provided", file=sys.stderr)
config.read(args.config) config.read(args.config)
if args.subcommand == "loop": if args.subcommand == "loop":
@@ -42,11 +46,6 @@ def main():
slabbedasker.main() slabbedasker.main()
elif args.subcommand == "seed-data":
import dibbler.subcommands.seed_test_data as seed_test_data
seed_test_data.main()
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -180,7 +180,7 @@ When finished, write an empty line to confirm the purchase.\n"""
print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr") print(f"User {t.user.name}'s credit is now {t.user.credit:d} kr")
if t.user.credit < config.getint("limits", "low_credit_warning_limit"): if t.user.credit < config.getint("limits", "low_credit_warning_limit"):
print( print(
f"USER {t.user.name} HAS LOWER CREDIT THAN {config.getint('limits', 'low_credit_warning_limit'):d},", f'USER {t.user.name} HAS LOWER CREDIT THAN {config.getint("limits", "low_credit_warning_limit"):d},',
"AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.", "AND SHOULD CONSIDER PUTTING SOME MONEY IN THE BOX.",
) )

View File

@@ -1,11 +1,11 @@
__all__ = [ __all__ = [
"Base", 'Base',
"Product", 'Product',
"Purchase", 'Purchase',
"PurchaseEntry", 'PurchaseEntry',
"Transaction", 'Transaction',
"User", 'User',
"UserProducts", 'UserProducts',
] ]
from .Base import Base from .Base import Base

View File

@@ -1,48 +0,0 @@
import json
from dibbler.db import Session
from pathlib import Path
from dibbler.models.Product import Product
from dibbler.models.User import User
JSON_FILE = Path(__file__).parent.parent.parent / "mock_data.json"
def clear_db(session):
session.query(Product).delete()
session.query(User).delete()
session.commit()
def main():
session = Session()
clear_db(session)
product_items = []
user_items = []
with open(JSON_FILE) as f:
json_obj = json.load(f)
for product in json_obj["products"]:
product_item = Product(
bar_code=product["bar_code"],
name=product["name"],
price=product["price"],
stock=product["stock"],
)
product_items.append(product_item)
for user in json_obj["users"]:
user_item = User(
name=user["name"],
card=user["card"],
rfid=user["rfid"],
credit=user["credit"],
)
user_items.append(user_item)
session.add_all(product_items)
session.add_all(user_items)
session.commit()

View File

@@ -1,18 +1,20 @@
[general] [general]
quit_allowed = true ; quit_allowed = false
stop_allowed = false ; stop_allowed = false
quit_allowed = true ; not for prod
stop_allowed = true ; not for prod
show_tracebacks = true show_tracebacks = true
input_encoding = 'utf8' input_encoding = 'utf8'
[database] [database]
# url = "postgresql://robertem@127.0.0.1/pvvvv" ; url = postgresql://dibbler:hunter2@127.0.0.1/pvvvv
url = "sqlite:///test.db" url = sqlite:///test.db ; devenv will override this to postgres using DIBBLER_DATABASE_URL
[limits] [limits]
low_credit_warning_limit = -100 low_credit_warning_limit = -100
user_recent_transaction_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 # Set rotate to False for endless labels
[printer] [printer]
label_type = "62" label_type = "62"

249
flake.lock generated
View File

@@ -1,5 +1,93 @@
{ {
"nodes": { "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": { "flake-utils": {
"inputs": { "inputs": {
"systems": "systems" "systems": "systems"
@@ -13,17 +101,117 @@
"type": "github" "type": "github"
}, },
"original": { "original": {
"id": "flake-utils", "owner": "numtide",
"type": "indirect" "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": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1749143949, "lastModified": 1730531603,
"narHash": "sha256-QuUtALJpVrPnPeozlUG/y+oIMSLdptHxb3GK6cpSVhA=", "narHash": "sha256-Dqg6si5CqIzm87sp57j5nTaeBbWhHFaVyG7V6L8k3lY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d3d2d80a2191a73d1e86456a751b83aa13085d7d", "rev": "7ffd9ae656aec493492b44d0ddfb28e79a1ea25d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -33,10 +221,59 @@
"type": "github" "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": { "root": {
"inputs": { "inputs": {
"devenv": "devenv",
"flake-utils": "flake-utils", "flake-utils": "flake-utils",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs_4"
} }
}, },
"systems": { "systems": {

151
flake.nix
View File

@@ -1,65 +1,128 @@
{ {
description = "Dibbler samspleisebod"; 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 nixConfig = {
inherit (nixpkgs) lib; extra-trusted-public-keys = [
"devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw="
];
extra-substituters = [
"https://devenv.cachix.org"
];
};
systems = [ outputs = { self, ... } @ inputs:
"x86_64-linux" inputs.flake-utils.lib.eachDefaultSystem (system: let
"aarch64-linux" pkgs = inputs.nixpkgs.legacyPackages.${system};
"x86_64-darwin" inherit (pkgs) lib;
"aarch64-darwin" in {
packages = {
default = self.packages.${system}.dibbler;
dibbler = pkgs.python311Packages.callPackage ./nix/dibbler.nix { };
skrot-vm = self.nixosConfigurations.skrot.config.system.build.vm;
# 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
]; ];
forAllSystems = f: lib.genAttrs systems (system: let services.postgres = {
pkgs = nixpkgs.legacyPackages.${system}; enable = true;
in f system pkgs); initialDatabases = [
in { {
packages = forAllSystems (system: pkgs: { name = "dibbler";
default = self.packages.${system}.dibbler; user = "dibbler";
dibbler = pkgs.callPackage ./nix/dibbler.nix { pass = "hunter2";
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;
};
});
overlays = {
default = self.overlays.dibbler;
dibbler = final: prev: {
inherit (self.packages.${prev.system}) dibbler;
}; };
}; };
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; 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"; system = "aarch64-linux";
pkgs = import nixpkgs {
inherit system;
overlays = [ self.overlays.dibbler ];
};
modules = [ 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 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" ];
# };
})
]; ];
}); };
}; };
} }

View File

@@ -1,76 +0,0 @@
{
"products": [
{
"product_id": 1,
"bar_code": "1234567890123",
"name": "Wireless Mouse",
"price": 2999,
"stock": 150,
"hidden": false
},
{
"product_id": 2,
"bar_code": "9876543210987",
"name": "Mechanical Keyboard",
"price": 5999,
"stock": 75,
"hidden": false
},
{
"product_id": 3,
"bar_code": "1112223334445",
"name": "Gaming Monitor",
"price": 19999,
"stock": 20,
"hidden": false
},
{
"product_id": 4,
"bar_code": "5556667778889",
"name": "USB-C Docking Station",
"price": 8999,
"stock": 50,
"hidden": true
},
{
"product_id": 5,
"bar_code": "4445556667771",
"name": "Noise Cancelling Headphones",
"price": 12999,
"stock": 30,
"hidden": true
}
],
"users": [
{
"name": "Albert",
"credit": 42069,
"card": "NTU12345678",
"rfid": "a1b2c3d4e5"
},
{
"name": "lorem",
"credit": 2000,
"card": "9876543210",
"rfid": "f6e7d8c9b0"
},
{
"name": "ibsum",
"credit": 1000,
"card": "11122233",
"rfid": ""
},
{
"name": "dave",
"credit": 7500,
"card": "NTU56789012",
"rfid": "1234abcd5678"
},
{
"name": "eve",
"credit": 3000,
"card": null,
"rfid": "deadbeef1234"
}
]
}

View File

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

View File

@@ -1,31 +1,16 @@
{ config, pkgs, lib, ... }: let { config, pkgs, lib, ... }: let
cfg = config.services.dibbler; cfg = config.services.dibbler;
format = pkgs.formats.ini { };
in { in {
options.services.dibbler = { options.services.dibbler = {
enable = lib.mkEnableOption "dibbler, the little kiosk computer";
package = lib.mkPackageOption pkgs "dibbler" { }; package = lib.mkPackageOption pkgs "dibbler" { };
config = lib.mkOption {
settings = lib.mkOption { default = ../conf.py;
description = "Configuration for dibbler";
default = { };
type = lib.types.submodule {
freeformType = format.type;
};
}; };
}; };
config = let config = let
screen = "${pkgs.screen}/bin/screen"; screen = "${pkgs.screen}/bin/screen";
in lib.mkIf cfg.enable { in {
services.dibbler.settings = lib.pipe ../example-config.ini [
builtins.readFile
builtins.fromTOML
(lib.mapAttrsRecursive (_: lib.mkDefault))
];
boot = { boot = {
consoleLogLevel = 0; consoleLogLevel = 0;
enableContainers = false; enableContainers = false;
@@ -38,7 +23,10 @@ in {
group = "dibbler"; group = "dibbler";
extraGroups = [ "lp" ]; extraGroups = [ "lp" ];
isNormalUser = true; 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";}
);
}; };
}; };
@@ -47,9 +35,7 @@ in {
wantedBy = [ "default.target" ]; wantedBy = [ "default.target" ];
serviceConfig = { serviceConfig = {
ExecStartPre = "-${screen} -X -S dibbler kill"; ExecStartPre = "-${screen} -X -S dibbler kill";
ExecStart = let ExecStart = "${screen} -dmS dibbler -O -l ${cfg.package}/bin/dibbler --config ${cfg.config} loop";
config = format.generate "dibbler-config.ini" cfg.settings;
in "${screen} -dmS dibbler -O -l ${cfg.package}/bin/dibbler --config ${config} loop";
ExecStartPost = "${screen} -X -S dibbler width 42 80"; ExecStartPost = "${screen} -X -S dibbler width 42 80";
User = "dibbler"; User = "dibbler";
Group = "dibbler"; Group = "dibbler";
@@ -72,6 +58,30 @@ in {
serviceConfig.Restart = "always"; # restart when session is closed serviceConfig.Restart = "always"; # restart when session is closed
}; };
services.getty.autologinUser = lib.mkForce "dibbler"; services = {
openssh = {
enable = true;
permitRootLogin = "yes";
};
getty.autologinUser = lib.mkForce "dibbler";
udisks2.enable = false;
};
networking.firewall.logRefusedConnections = false;
console.keyMap = "no";
programs.command-not-found.enable = false;
i18n.supportedLocales = [ "en_US.UTF-8/UTF-8" ];
environment.noXlibs = true;
documentation = {
info.enable = false;
man.enable = false;
};
security = {
polkit.enable = lib.mkForce false;
audit.enable = false;
};
}; };
} }

View File

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

View File

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

View File

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