Skip to content

Commit

Permalink
Add nocodb for health database.
Browse files Browse the repository at this point in the history
  • Loading branch information
pippijn committed Sep 18, 2024
1 parent 90fca68 commit 6152549
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 4 deletions.
9 changes: 8 additions & 1 deletion .config/home-manager/home.nix
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{ config, pkgs, ... }:
{ config, pkgs, lib, ... }:

let
sys = (import <nixpkgs/nixos> { }).config;
Expand All @@ -15,6 +15,7 @@ in {
git-crypt # encrypted files in public git repos
jq # json query tool
keychain # ssh-agent
python3 # various scripts
rclone # sync with nextcloud
screen # terminal window manager
unison # sync with other machines
Expand All @@ -33,6 +34,12 @@ in {

home.sessionVariables = {
EDITOR = config.programs.zsh.shellAliases.vi;

NOCODB_TOKEN =
let tokenFile = "${config.home.homeDirectory}/.nocodb"; in
if builtins.pathExists tokenFile
then lib.removeSuffix "\n" (builtins.readFile tokenFile)
else "";
};

programs.gpg.enable = true;
Expand Down
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/.nocodb filter=git-crypt diff=git-crypt
/.pvs-license filter=git-crypt diff=git-crypt
/.reminders filter=git-crypt diff=git-crypt
Binary file added .nocodb
Binary file not shown.
2 changes: 1 addition & 1 deletion code/kubes/nextcloud/nextcloud/nextcloud-ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ metadata:
name: nextcloud-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: 20m
nginx.ingress.kubernetes.io/proxy-body-size: "4096m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
spec:
ingressClassName: nginx
Expand Down
5 changes: 3 additions & 2 deletions code/kubes/nextcloud/nextcloud/nginx-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ data:
application/javascript mjs;
}
# set max upload size
client_max_body_size 4096M;
sendfile on;
#tcp_nopush on;
Expand All @@ -39,8 +42,6 @@ data:
server {
listen 80;
# set max upload size
client_max_body_size 512M;
fastcgi_buffers 64 4K;
# Enable gzip but do not remove ETag headers
Expand Down
7 changes: 7 additions & 0 deletions code/kubes/nocodb/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
kind: Namespace
apiVersion: v1
metadata:
name: nocodb
annotations:
linkerd.io/inject: enabled
27 changes: 27 additions & 0 deletions code/kubes/nocodb/nocodb/nocodb-ingress.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: nocodb
name: nocodb-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/proxy-body-size: "4096m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
spec:
ingressClassName: nginx
tls:
- hosts:
- nocodb.xinutec.org
secretName: nocodb-tls
rules:
- host: nocodb.xinutec.org
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nocodb-server
port:
number: 8080
44 changes: 44 additions & 0 deletions code/kubes/nocodb/nocodb/nocodb-server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: nocodb
name: nocodb-server
labels:
app: nocodb
spec:
selector:
matchLabels:
pod-label: nocodb-server-pod
template:
metadata:
labels:
pod-label: nocodb-server-pod
spec:
volumes:
# Create the shared files volume to be used in both pods
- name: server-storage
persistentVolumeClaim:
claimName: nocodb-storage
containers:
- name: nocodb
image: nocodb/nocodb:latest
imagePullPolicy: Always
volumeMounts:
- name: server-storage
mountPath: /usr/app/data
subPath: server-data
---
apiVersion: v1
kind: Service
metadata:
namespace: nocodb
name: nocodb-server
labels:
app: nocodb
spec:
selector:
pod-label: nocodb-server-pod
ports:
- protocol: TCP
port: 8080
14 changes: 14 additions & 0 deletions code/kubes/nocodb/nocodb/nocodb-shared-pvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
namespace: nocodb
name: nocodb-storage
labels:
app: nocodb
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 200Gi
109 changes: 109 additions & 0 deletions code/kubes/nocodb/today.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env python3

import http.client
import json
import os
import urllib.parse

RDI_TABLE = "mamm9dpys1j1znw"

DISHES_TABLE = "m5ddsw6b8q0jn55"

MEALS_TABLE = "mohjrlr1l7et77g"
MEALS_VIEW_TODAY = "vwpckbejvg1mbifx"
MEALS_LINK_DISHES = "cc9wqhoe8nfb18o"

def process_nutrients(rdi, ingredient):
nutrients = {}
ingredient_amount = ingredient["Amount (g)"]
for k, v in ingredient.items():
if v is None or k in ("Amount (g)", "Name"):
nutrients[k] = v
elif k in rdi:
amount = v / 100 * ingredient_amount
nutrients[k] = {"Amount": amount, "RDI": amount / rdi[k]["Amount"]}
return nutrients


def sum_amounts(total, dishes):
for dish in dishes:
for k, v in dish.items():
if isinstance(v, dict):
if k not in total:
total[k] = {"Amount": 0, "RDI": 0}
total[k]["Amount"] += v["Amount"]
total[k]["RDI"] += v["RDI"]


def compute_total(dishes):
total = {"Name": "Total", "Amount (g)": None}
sum_amounts(total, dishes)
dishes.append(total)
return dishes


class HealthDb:
def __init__(self):
self.conn = http.client.HTTPSConnection("nocodb.xinutec.org")
self.headers = {'xc-token': os.environ["NOCODB_TOKEN"]}

def get(self, url: str):
self.conn.request("GET", url, headers=self.headers)

res = self.conn.getresponse()
return json.loads(res.read().decode("utf-8"))["list"]

def list_table_records(self, table: str, view: str = "", fields: tuple[str] = tuple(), key: str = "Id"):
return {
rec[key]: rec
for rec in self.get(f"/api/v2/tables/{table}/records?offset=0&limit=1000&viewId={view}&fields={','.join(map(urllib.parse.quote_plus, fields))}")
}

def list_linked_records(self, table: str, link_field_id: str, record_id: int, key: str = "Id"):
return {rec[key]: rec for rec in self.get(f"/api/v2/tables/{table}/links/{link_field_id}/records/{record_id}")}

def today(self):
rdi = self.list_table_records(RDI_TABLE, fields=("Nutrient", "Amount"), key="Nutrient")
dishes = self.list_table_records(DISHES_TABLE, fields=("Id", "Amount (g)", "nc_cupq___nc_m2m_8u3b_5gyns"))
meals = self.list_table_records(MEALS_TABLE, view=MEALS_VIEW_TODAY)
for mealId in [meal["Id"] for meal in meals.values()]:
meals[mealId]["Dishes"] = [process_nutrients(rdi, {
"Amount (g)": dishes[k]["Amount (g)"],
**dishes[k]["nc_cupq___nc_m2m_8u3b_5gyns"][0]["Ingredients"]
}) for k in self.list_linked_records(MEALS_TABLE, MEALS_LINK_DISHES, mealId).keys()]
table = {}
for meal in meals.values():
table[meal["Meal"]] = compute_total(meal["Dishes"])
return table


def format_amount(amount):
if isinstance(amount, dict):
rdi = int(round(amount['RDI'] * 100, 0))
return f"{round(amount['Amount'], 2):7} ({rdi:3}%)"
elif isinstance(amount, int):
return f"{round(amount, 2):7}"
else:
return ""


def print_dishes(meal, dishes):
print(f"\n## {meal}\n")
keys = [k for k in dishes[0].keys() if k not in ("Name",)]
padding = max(len(k) for k in keys)
title = "| " + " " * padding + " | " + " | ".join(f"{dish['Name']:18}" for dish in dishes)
print(title)
print("| :" + "-" * (padding - 1) + " | " + " | ".join("-" * 17 + ":" for dish in dishes))
for k in keys:
print(f"| {k:{padding}} | " + " | ".join(f"{format_amount(dish.get(k, None)):18}" for dish in dishes))

db = HealthDb()

total = {"Name": "Total"}

for meal, dishes in db.today().items():
print_dishes(meal, dishes)

sum_amounts(total, (dish for dish in dishes if dish["Name"] == "Total"))

print_dishes("Total", [total])

0 comments on commit 6152549

Please sign in to comment.