Restructure project #3
8
.gitignore
vendored
8
.gitignore
vendored
@ -1,2 +1,8 @@
|
||||
result
|
||||
result-*
|
||||
result-*
|
||||
|
||||
dist
|
||||
|
||||
test.db
|
||||
|
||||
.ruff_cache
|
30
ALTER
30
ALTER
@ -1,30 +0,0 @@
|
||||
torjehoa_dibblerdummy=> ALTER TABLE products ADD stock integer;
|
||||
|
||||
Table "torjehoa_dibblerdummy.products"
|
||||
Column | Type | Modifiers
|
||||
----------+-----------------------+-----------
|
||||
bar_code | character varying(13) | not null
|
||||
name | character varying(45) |
|
||||
price | integer |
|
||||
Indexes:
|
||||
"products_pkey" PRIMARY KEY, btree (bar_code)
|
||||
|
||||
torjehoa_dibblerdummy=> ALTER TABLE products ADD stock integer;
|
||||
ALTER TABLE
|
||||
torjehoa_dibblerdummy=> UPDATE products SET stock = 0;
|
||||
UPDATE 102
|
||||
torjehoa_dibblerdummy=> ALTER TABLE products ALTER stock SET NOT NULL;
|
||||
ALTER TABLE
|
||||
torjehoa_dibblerdummy=> \d products
|
||||
Table "torjehoa_dibblerdummy.products"
|
||||
Column | Type | Modifiers
|
||||
----------+-----------------------+-----------
|
||||
bar_code | character varying(13) | not null
|
||||
name | character varying(45) |
|
||||
price | integer |
|
||||
stock | integer | not null
|
||||
Indexes:
|
||||
"products_pkey" PRIMARY KEY, btree (bar_code)
|
||||
|
||||
torjehoa_dibblerdummy=>
|
||||
|
4
conf.py
4
conf.py
@ -1,8 +1,8 @@
|
||||
db_url = 'postgresql://robertem@127.0.0.1/pvvvv'
|
||||
db_url = "postgresql://robertem@127.0.0.1/pvvvv"
|
||||
quit_allowed = True
|
||||
stop_allowed = False
|
||||
show_tracebacks = True
|
||||
input_encoding = 'utf8'
|
||||
input_encoding = "utf8"
|
||||
|
||||
low_credit_warning_limit = -100
|
||||
user_recent_transaction_limit = 100
|
||||
|
176
db.py
176
db.py
@ -1,176 +0,0 @@
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, create_engine, DateTime, Boolean
|
||||
from sqlalchemy.orm import sessionmaker, relationship, backref
|
||||
from sqlalchemy.ext.declarative import declarative_base
|
||||
from math import ceil, floor
|
||||
import datetime
|
||||
import conf
|
||||
|
||||
engine = create_engine(conf.db_url)
|
||||
Base = declarative_base()
|
||||
Session = sessionmaker(bind=engine)
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'users'
|
||||
name = Column(String(10), primary_key=True)
|
||||
card = Column(String(20))
|
||||
rfid = Column(String(20))
|
||||
credit = Column(Integer)
|
||||
|
||||
name_re = r"[a-z]+"
|
||||
card_re = r"(([Nn][Tt][Nn][Uu])?[0-9]+)?"
|
||||
rfid_re = r"[0-9a-fA-F]*"
|
||||
|
||||
def __init__(self, name, card, rfid=None, credit=0):
|
||||
self.name = name
|
||||
if card == '':
|
||||
card = None
|
||||
self.card = card
|
||||
if rfid == '':
|
||||
rfid = None
|
||||
self.rfid = rfid
|
||||
self.credit = credit
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User('{self.name}')>"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def is_anonymous(self):
|
||||
return self.card == '11122233'
|
||||
|
||||
|
||||
class Product(Base):
|
||||
__tablename__ = 'products'
|
||||
|
||||
product_id = Column(Integer, primary_key=True)
|
||||
bar_code = Column(String(13))
|
||||
name = Column(String(45))
|
||||
price = Column(Integer)
|
||||
stock = Column(Integer)
|
||||
hidden = Column(Boolean, nullable=False, default=False)
|
||||
|
||||
bar_code_re = r"[0-9]+"
|
||||
name_re = r".+"
|
||||
name_length = 45
|
||||
|
||||
def __init__(self, bar_code, name, price, stock=0, hidden = False):
|
||||
self.name = name
|
||||
self.bar_code = bar_code
|
||||
self.price = price
|
||||
self.stock = stock
|
||||
self.hidden = hidden
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Product('{self.name}', '{self.bar_code}', '{self.price}', '{self.stock}', '{self.hidden}')>"
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class UserProducts(Base):
|
||||
__tablename__ = 'user_products'
|
||||
user_name = Column(String(10), ForeignKey('users.name'), primary_key=True)
|
||||
product_id = Column(Integer, ForeignKey("products.product_id"), primary_key=True)
|
||||
count = Column(Integer)
|
||||
sign = Column(Integer)
|
||||
|
||||
user = relationship(User, backref=backref('products', order_by=count.desc()), lazy='joined')
|
||||
product = relationship(Product, backref="users", lazy='joined')
|
||||
|
||||
|
||||
class PurchaseEntry(Base):
|
||||
__tablename__ = 'purchase_entries'
|
||||
id = Column(Integer, primary_key=True)
|
||||
purchase_id = Column(Integer, ForeignKey("purchases.id"))
|
||||
product_id = Column(Integer, ForeignKey("products.product_id"))
|
||||
amount = Column(Integer)
|
||||
|
||||
product = relationship(Product, backref="purchases")
|
||||
|
||||
def __init__(self, purchase, product, amount):
|
||||
self.product = product
|
||||
self.product_bar_code = product.bar_code
|
||||
self.purchase = purchase
|
||||
self.amount = amount
|
||||
|
||||
def __repr__(self):
|
||||
return f"<PurchaseEntry('{self.product.name}', '{self.amount}')>"
|
||||
|
||||
|
||||
class Transaction(Base):
|
||||
__tablename__ = 'transactions'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
time = Column(DateTime)
|
||||
user_name = Column(String(10), ForeignKey('users.name'))
|
||||
amount = Column(Integer)
|
||||
description = Column(String(50))
|
||||
purchase_id = Column(Integer, ForeignKey('purchases.id'))
|
||||
penalty = Column(Integer)
|
||||
|
||||
user = relationship(User, backref=backref('transactions', order_by=time))
|
||||
|
||||
def __init__(self, user, amount=0, description=None, purchase=None, penalty=1):
|
||||
self.user = user
|
||||
self.amount = amount
|
||||
self.description = description
|
||||
self.purchase = purchase
|
||||
self.penalty = penalty
|
||||
|
||||
def perform_transaction(self, ignore_penalty=False):
|
||||
self.time = datetime.datetime.now()
|
||||
if not ignore_penalty:
|
||||
self.amount *= self.penalty
|
||||
self.user.credit -= self.amount
|
||||
|
||||
|
||||
class Purchase(Base):
|
||||
__tablename__ = 'purchases'
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
time = Column(DateTime)
|
||||
price = Column(Integer)
|
||||
|
||||
transactions = relationship(Transaction, order_by=Transaction.user_name, backref='purchase')
|
||||
entries = relationship(PurchaseEntry, backref=backref("purchase"))
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Purchase({int(self.id):d}, {self.price:d}, '{self.time.strftime('%c')}')>"
|
||||
|
||||
def is_complete(self):
|
||||
return len(self.transactions) > 0 and len(self.entries) > 0
|
||||
|
||||
def price_per_transaction(self, round_up=True):
|
||||
if round_up:
|
||||
return int(ceil(float(self.price)/len(self.transactions)))
|
||||
else:
|
||||
return int(floor(float(self.price)/len(self.transactions)))
|
||||
|
||||
def set_price(self, round_up=True):
|
||||
self.price = 0
|
||||
for entry in self.entries:
|
||||
self.price += entry.amount*entry.product.price
|
||||
if len(self.transactions) > 0:
|
||||
for t in self.transactions:
|
||||
t.amount = self.price_per_transaction(round_up=round_up)
|
||||
|
||||
def perform_purchase(self, ignore_penalty=False, round_up=True):
|
||||
self.time = datetime.datetime.now()
|
||||
self.set_price(round_up=round_up)
|
||||
for t in self.transactions:
|
||||
t.perform_transaction(ignore_penalty=ignore_penalty)
|
||||
for entry in self.entries:
|
||||
entry.product.stock -= entry.amount
|
||||
|
||||
def perform_soft_purchase(self, price, round_up=True):
|
||||
self.time = datetime.datetime.now()
|
||||
self.price = price
|
||||
for t in self.transactions:
|
||||
t.amount = self.price_per_transaction(round_up=round_up)
|
||||
for t in self.transactions:
|
||||
t.perform_transaction()
|
@ -1,7 +1,4 @@
|
||||
{ pkgs ? import <nixos-unstable> { } }:
|
||||
|
||||
rec {
|
||||
|
||||
{
|
||||
dibbler = pkgs.callPackage ./nix/dibbler.nix { };
|
||||
|
||||
}
|
||||
|
0
dibbler/__init__.py
Normal file
0
dibbler/__init__.py
Normal file
6
dibbler/conf.py
Normal file
6
dibbler/conf.py
Normal file
@ -0,0 +1,6 @@
|
||||
# This module is supposed to act as a singleton and be filled
|
||||
# with config variables by cli.py
|
||||
|
||||
import configparser
|
||||
|
||||
config = configparser.ConfigParser()
|
7
dibbler/db.py
Normal file
7
dibbler/db.py
Normal file
@ -0,0 +1,7 @@
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
from dibbler.conf import config
|
||||
|
||||
engine = create_engine(config.get("database", "url"))
|
||||
Session = sessionmaker(bind=engine)
|
@ -6,20 +6,20 @@ from brother_ql.devicedependent import label_type_specs
|
||||
|
||||
|
||||
def px2mm(px, dpi=300):
|
||||
return (25.4 * px)/dpi
|
||||
return (25.4 * px) / dpi
|
||||
|
||||
|
||||
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__()
|
||||
assert typ in label_type_specs
|
||||
self.rot = rot
|
||||
if self.rot:
|
||||
self._h, self._w = label_type_specs[typ]['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_type_specs[typ]['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
|
||||
@ -31,36 +31,40 @@ class BrotherLabelWriter(ImageWriter):
|
||||
super(BrotherLabelWriter, self)._init(code)
|
||||
|
||||
def calculate_size(self, modules_per_line, number_of_lines, dpi=300):
|
||||
x, y = super(BrotherLabelWriter, self).calculate_size(modules_per_line, number_of_lines, dpi)
|
||||
x, y = super(BrotherLabelWriter, self).calculate_size(
|
||||
modules_per_line, number_of_lines, dpi
|
||||
)
|
||||
|
||||
self._xo = (px2mm(self._w)-px2mm(x))/2
|
||||
self._yo = (px2mm(self._h)-px2mm(y))
|
||||
self._xo = (px2mm(self._w) - px2mm(x)) / 2
|
||||
self._yo = px2mm(self._h) - px2mm(y)
|
||||
assert self._xo >= 0
|
||||
assert self._yo >= 0
|
||||
|
||||
return int(self._w), int(self._h)
|
||||
|
||||
def _paint_module(self, xpos, ypos, width, color):
|
||||
super(BrotherLabelWriter, self)._paint_module(xpos+self._xo, ypos+self._yo, width, color)
|
||||
super(BrotherLabelWriter, self)._paint_module(
|
||||
xpos + self._xo, ypos + self._yo, width, color
|
||||
)
|
||||
|
||||
def _paint_text(self, xpos, ypos):
|
||||
super(BrotherLabelWriter, self)._paint_text(xpos+self._xo, ypos+self._yo)
|
||||
super(BrotherLabelWriter, self)._paint_text(xpos + self._xo, ypos + self._yo)
|
||||
|
||||
def _finish(self):
|
||||
if self._title:
|
||||
width = self._w+1
|
||||
width = self._w + 1
|
||||
height = 0
|
||||
max_h = self._h - mm2px(self._yo, self.dpi)
|
||||
fs = int(max_h / 1.2)
|
||||
font_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "Stranger back in the Night.ttf")
|
||||
font_path = os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
"Stranger back in the Night.ttf",
|
||||
)
|
||||
font = ImageFont.truetype(font_path, 10)
|
||||
while width > self._w or height > max_h:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
width, height = font.getsize(self._title)
|
||||
fs -= 1
|
||||
pos = (
|
||||
(self._w-width)//2,
|
||||
0 - (height // 8)
|
||||
)
|
||||
pos = ((self._w - width) // 2, 0 - (height // 8))
|
||||
self._draw.text(pos, self._title, font=font, fill=self.foreground)
|
||||
return self._image
|
130
dibbler/lib/helpers.py
Normal file
130
dibbler/lib/helpers.py
Normal file
@ -0,0 +1,130 @@
|
||||
import pwd
|
||||
import subprocess
|
||||
import os
|
||||
import signal
|
||||
|
||||
from sqlalchemy import or_, and_
|
||||
|
||||
from ..models import User, Product
|
||||
|
||||
|
||||
def search_user(string, session, ignorethisflag=None):
|
||||
string = string.lower()
|
||||
exact_match = (
|
||||
session.query(User)
|
||||
.filter(or_(User.name == string, User.card == string, User.rfid == string))
|
||||
.first()
|
||||
)
|
||||
if exact_match:
|
||||
return exact_match
|
||||
user_list = (
|
||||
session.query(User)
|
||||
.filter(
|
||||
or_(
|
||||
User.name.ilike(f"%{string}%"),
|
||||
User.card.ilike(f"%{string}%"),
|
||||
User.rfid.ilike(f"%{string}%"),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
return user_list
|
||||
|
||||
|
||||
def search_product(string, session, find_hidden_products=True):
|
||||
if find_hidden_products:
|
||||
exact_match = (
|
||||
session.query(Product)
|
||||
.filter(or_(Product.bar_code == string, Product.name == string))
|
||||
.first()
|
||||
)
|
||||
else:
|
||||
exact_match = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code == string,
|
||||
and_(Product.name == string, Product.hidden is False),
|
||||
)
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if exact_match:
|
||||
return exact_match
|
||||
if find_hidden_products:
|
||||
product_list = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code.ilike(f"%{string}%"),
|
||||
Product.name.ilike(f"%{string}%"),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
product_list = (
|
||||
session.query(Product)
|
||||
.filter(
|
||||
or_(
|
||||
Product.bar_code.ilike(f"%{string}%"),
|
||||
and_(Product.name.ilike(f"%{string}%"), Product.hidden is False),
|
||||
)
|
||||
)
|
||||
.all()
|
||||
)
|
||||
return product_list
|
||||
|
||||
|
||||
def system_user_exists(username):
|
||||
try:
|
||||
pwd.getpwnam(username)
|
||||
except KeyError:
|
||||
return False
|
||||
except UnicodeEncodeError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def guess_data_type(string):
|
||||
if string.startswith("ntnu") and string[4:].isdigit():
|
||||
return "card"
|
||||
if string.isdigit() and len(string) == 10:
|
||||
return "rfid"
|
||||
if string.isdigit() and len(string) in [8, 13]:
|
||||
return "bar_code"
|
||||
# if string.isdigit() and len(string) > 5:
|
||||
# return 'card'
|
||||
if string.isalpha() and string.islower() and system_user_exists(string):
|
||||
return "username"
|
||||
return None
|
||||
|
||||
|
||||
def argmax(d, all=False, value=None):
|
||||
maxarg = None
|
||||
if value is not None:
|
||||
dd = d
|
||||
d = {}
|
||||
for key in list(dd.keys()):
|
||||
d[key] = value(dd[key])
|
||||
for key in list(d.keys()):
|
||||
if maxarg is None or d[key] > d[maxarg]:
|
||||
maxarg = key
|
||||
if all:
|
||||
return [k for k in list(d.keys()) if d[k] == d[maxarg]]
|
||||
return maxarg
|
||||
|
||||
|
||||
def less(string):
|
||||
"""
|
||||
Run less with string as input; wait until it finishes.
|
||||
"""
|
||||
# If we don't ignore SIGINT while running the `less` process,
|
||||
# it will become a zombie when someone presses C-c.
|
||||
int_handler = signal.signal(signal.SIGINT, signal.SIG_IGN)
|
||||
env = dict(os.environ)
|
||||
env["LESSSECURE"] = "1"
|
||||
proc = subprocess.Popen("less", env=env, encoding="utf-8", stdin=subprocess.PIPE)
|
||||
proc.communicate(string)
|
||||
signal.signal(signal.SIGINT, int_handler)
|
@ -1,47 +1,50 @@
|
||||
import os
|
||||
import datetime
|
||||
|
||||
import barcode
|
||||
import datetime
|
||||
from PIL import Image
|
||||
from PIL import ImageDraw
|
||||
from PIL import ImageFont
|
||||
from brother_ql import BrotherQLRaster
|
||||
from brother_ql import create_label
|
||||
from brother_ql import BrotherQLRaster, create_label
|
||||
from brother_ql.backends import backend_factory
|
||||
from brother_ql.devicedependent import label_type_specs
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
from barcode_helpers import BrotherLabelWriter
|
||||
from .barcode_helpers import BrotherLabelWriter
|
||||
|
||||
|
||||
def print_name_label(text, margin=10, rotate=False, label_type="62", printer_type="QL-700",):
|
||||
def print_name_label(
|
||||
text,
|
||||
margin=10,
|
||||
rotate=False,
|
||||
label_type="62",
|
||||
printer_type="QL-700",
|
||||
):
|
||||
if not rotate:
|
||||
width, height = label_type_specs[label_type]['dots_printable']
|
||||
width, height = label_type_specs[label_type]["dots_printable"]
|
||||
else:
|
||||
height, width = label_type_specs[label_type]['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
|
||||
tw, th = width, height
|
||||
if width == 0:
|
||||
while th + 2*margin > height:
|
||||
while th + 2 * margin > height:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
width = tw+2*margin
|
||||
width = tw + 2 * margin
|
||||
elif height == 0:
|
||||
while tw + 2*margin > width:
|
||||
while tw + 2 * margin > width:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
height = th+2*margin
|
||||
height = th + 2 * margin
|
||||
else:
|
||||
while tw + 2*margin > width or th + 2*margin > height:
|
||||
while tw + 2 * margin > width or th + 2 * margin > height:
|
||||
font = ImageFont.truetype(font_path, fs)
|
||||
tw, th = font.getsize(text)
|
||||
fs -= 1
|
||||
|
||||
xp = (width//2)-(tw//2)
|
||||
yp = (height//2)-(th//2)
|
||||
xp = (width // 2) - (tw // 2)
|
||||
yp = (height // 2) - (th // 2)
|
||||
|
||||
im = Image.new("RGB", (width, height), (255, 255, 255))
|
||||
dr = ImageDraw.Draw(im)
|
||||
@ -58,8 +61,14 @@ def print_name_label(text, margin=10, rotate=False, label_type="62", printer_typ
|
||||
print_image(fn, printer_type, label_type)
|
||||
|
||||
|
||||
def print_bar_code(barcode_value, barcode_text, barcode_type="ean13", rotate=False, printer_type="QL-700",
|
||||
label_type="62"):
|
||||
def print_bar_code(
|
||||
barcode_value,
|
||||
barcode_text,
|
||||
barcode_type="ean13",
|
||||
rotate=False,
|
||||
printer_type="QL-700",
|
||||
label_type="62",
|
||||
):
|
||||
bar_coder = barcode.get_barcode_class(barcode_type)
|
||||
wr = BrotherLabelWriter(typ=label_type, rot=rotate, text=barcode_text, max_height=1000)
|
||||
|
||||
@ -75,12 +84,12 @@ def print_image(fn, printer_type="QL-700", label_type="62"):
|
||||
create_label(qlr, fn, label_type, threshold=70, cut=True)
|
||||
|
||||
be = backend_factory("pyusb")
|
||||
list_available_devices = be['list_available_devices']
|
||||
BrotherQLBackend = be['backend_class']
|
||||
list_available_devices = be["list_available_devices"]
|
||||
BrotherQLBackend = be["backend_class"]
|
||||
|
||||
ad = list_available_devices()
|
||||
assert ad
|
||||
string_descr = ad[0]['string_descr']
|
||||
string_descr = ad[0]["string_descr"]
|
||||
|
||||
printer = BrotherQLBackend(string_descr)
|
||||
|
512
dibbler/lib/statistikkHelpers.py
Normal file
512
dibbler/lib/statistikkHelpers.py
Normal file
@ -0,0 +1,512 @@
|
||||
#! /usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
|
||||
import datetime
|
||||
from collections import defaultdict
|
||||
|
||||
from .helpers import *
|
||||
from ..models import Transaction
|
||||
from ..db import Session
|
||||
|
||||
|
||||
def getUser():
|
||||
while 1:
|
||||
string = input("user? ")
|
||||
session = Session()
|
||||
user = search_user(string, session)
|
||||
session.close()
|
||||
if not isinstance(user, list):
|
||||
return user.name
|
||||
i = 0
|
||||
if len(user) == 0:
|
||||
print("no matching string")
|
||||
if len(user) == 1:
|
||||
print("antar: ", user[0].name, "\n")
|
||||
return user[0].name
|
||||
if len(user) > 10:
|
||||
continue
|
||||
for u in user:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input("enter number:"))
|
||||
except:
|
||||
print("invalid input, restarting")
|
||||
continue
|
||||
if (n > -1) and (n < i):
|
||||
return user[n].name
|
||||
|
||||
|
||||
def getProduct():
|
||||
while 1:
|
||||
string = input("product? ")
|
||||
session = Session()
|
||||
product = search_product(string, session)
|
||||
session.close()
|
||||
if not isinstance(product, list):
|
||||
return product.name
|
||||
i = 0
|
||||
if len(product) == 0:
|
||||
print("no matching string")
|
||||
if len(product) == 1:
|
||||
print("antar: ", product[0].name, "\n")
|
||||
return product[0].name
|
||||
if len(product) > 10:
|
||||
continue
|
||||
for u in product:
|
||||
print(i, u.name)
|
||||
i += 1
|
||||
try:
|
||||
n = int(input("enter number:"))
|
||||
except:
|
||||
print("invalid input, restarting")
|
||||
continue
|
||||
if (n > -1) and (n < i):
|
||||
return product[n].name
|
||||
|
||||
|
||||
class Database:
|
||||
# for varer
|
||||
varePersonAntall = defaultdict(dict) # varePersonAntall[Oreo][trygvrad] == 3
|
||||
vareDatoAntall = defaultdict(list) # dict->array
|
||||
vareUkedagAntall = defaultdict(list)
|
||||
# for personer
|
||||
personVareAntall = defaultdict(dict) # personVareAntall[trygvrad][Oreo] == 3
|
||||
personVareVerdi = defaultdict(dict) # personVareVerdi[trygvrad][Oreo] == 30 #[kr]
|
||||
personDatoVerdi = defaultdict(list) # dict->array
|
||||
personUkedagVerdi = defaultdict(list)
|
||||
# for global
|
||||
personPosTransactions = (
|
||||
{}
|
||||
) # personPosTransactions[trygvrad] == 100 #trygvrad har lagt 100kr i boksen
|
||||
personNegTransactions = (
|
||||
{}
|
||||
) # personNegTransactions[trygvrad» == 70 #trygvrad har tatt 70kr fra boksen
|
||||
globalVareAntall = {} # globalVareAntall[Oreo] == 3
|
||||
globalVareVerdi = {} # globalVareVerdi[Oreo] == 30 #[kr]
|
||||
globalPersonAntall = {} # globalPersonAntall[trygvrad] == 3
|
||||
globalPersonForbruk = {} # globalPersonVerdi == 30 #[kr]
|
||||
globalUkedagForbruk = []
|
||||
globalDatoVarer = []
|
||||
globalDatoForbruk = []
|
||||
pengebeholdning = []
|
||||
|
||||
|
||||
class InputLine:
|
||||
def __init__(self, u, p, t):
|
||||
self.inputUser = u
|
||||
self.inputProduct = p
|
||||
self.inputType = t
|
||||
|
||||
|
||||
def getDateDb(date, inp):
|
||||
try:
|
||||
year = inp.partition("-")
|
||||
month = year[2].partition("-")
|
||||
return datetime.datetime(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print("invalid date, setting date to date found in db")
|
||||
print(date)
|
||||
return date
|
||||
|
||||
|
||||
def dateToDateNumDb(date, startDate):
|
||||
deltaDays = date - startDate
|
||||
return int(deltaDays.days), date.weekday()
|
||||
|
||||
|
||||
def getInputType():
|
||||
inp = 0
|
||||
while not (inp == "1" or inp == "2" or inp == "3" or inp == "4"):
|
||||
print("type 1 for user-statistics")
|
||||
print("type 2 for product-statistics")
|
||||
print("type 3 for global-statistics")
|
||||
print("type 4 to enter loop-mode")
|
||||
inp = input("")
|
||||
return int(inp)
|
||||
|
||||
|
||||
def getProducts(products):
|
||||
product = []
|
||||
products = products.partition("¤")
|
||||
product.append(products[0])
|
||||
while products[1] == "¤":
|
||||
products = products[2].partition("¤")
|
||||
product.append(products[0])
|
||||
return product
|
||||
|
||||
|
||||
def getDateFile(date, inp):
|
||||
try:
|
||||
year = inp.partition("-")
|
||||
month = year[2].partition("-")
|
||||
return datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
except:
|
||||
print("invalid date, setting date to date found on file file")
|
||||
print(date)
|
||||
return datetime.date(
|
||||
int(date.partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[0]),
|
||||
int(date.partition("-")[2].partition("-")[2]),
|
||||
)
|
||||
|
||||
|
||||
def dateToDateNumFile(date, startDate):
|
||||
year = date.partition("-")
|
||||
month = year[2].partition("-")
|
||||
day = datetime.date(int(year[0]), int(month[0]), int(month[2]))
|
||||
deltaDays = day - startDate
|
||||
return int(deltaDays.days), day.weekday()
|
||||
|
||||
|
||||
def clearDatabase(database):
|
||||
database.varePersonAntall.clear()
|
||||
database.vareDatoAntall.clear()
|
||||
database.vareUkedagAntall.clear()
|
||||
database.personVareAntall.clear()
|
||||
database.personVareVerdi.clear()
|
||||
database.personDatoVerdi.clear()
|
||||
database.personUkedagVerdi.clear()
|
||||
database.personPosTransactions.clear()
|
||||
database.personNegTransactions.clear()
|
||||
database.globalVareAntall.clear()
|
||||
database.globalVareVerdi.clear()
|
||||
database.globalPersonAntall.clear()
|
||||
database.globalPersonForbruk.clear()
|
||||
return database
|
||||
|
||||
|
||||
def addLineToDatabase(database, inputLine):
|
||||
if abs(inputLine.price) > 90000:
|
||||
return database
|
||||
# fyller inn for varer
|
||||
if (not inputLine.product == "") and (
|
||||
(inputLine.inputProduct == "") or (inputLine.inputProduct == inputLine.product)
|
||||
):
|
||||
database.varePersonAntall[inputLine.product][inputLine.user] = (
|
||||
database.varePersonAntall[inputLine.product].setdefault(inputLine.user, 0) + 1
|
||||
)
|
||||
if inputLine.product not in database.vareDatoAntall:
|
||||
database.vareDatoAntall[inputLine.product] = [0] * (inputLine.numberOfDays + 1)
|
||||
database.vareDatoAntall[inputLine.product][inputLine.dateNum] += 1
|
||||
if inputLine.product not in database.vareUkedagAntall:
|
||||
database.vareUkedagAntall[inputLine.product] = [0] * 7
|
||||
database.vareUkedagAntall[inputLine.product][inputLine.weekday] += 1
|
||||
# fyller inn for personer
|
||||
if (inputLine.inputUser == "") or (inputLine.inputUser == inputLine.user):
|
||||
if not inputLine.product == "":
|
||||
database.personVareAntall[inputLine.user][inputLine.product] = (
|
||||
database.personVareAntall[inputLine.user].setdefault(inputLine.product, 0) + 1
|
||||
)
|
||||
database.personVareVerdi[inputLine.user][inputLine.product] = (
|
||||
database.personVareVerdi[inputLine.user].setdefault(inputLine.product, 0)
|
||||
+ inputLine.price
|
||||
)
|
||||
if inputLine.user not in database.personDatoVerdi:
|
||||
database.personDatoVerdi[inputLine.user] = [0] * (inputLine.numberOfDays + 1)
|
||||
database.personDatoVerdi[inputLine.user][inputLine.dateNum] += inputLine.price
|
||||
if inputLine.user not in database.personUkedagVerdi:
|
||||
database.personUkedagVerdi[inputLine.user] = [0] * 7
|
||||
database.personUkedagVerdi[inputLine.user][inputLine.weekday] += inputLine.price
|
||||
# fyller inn delt statistikk (genereres uansett)
|
||||
if inputLine.product == "":
|
||||
if inputLine.price > 0:
|
||||
database.personPosTransactions[inputLine.user] = (
|
||||
database.personPosTransactions.setdefault(inputLine.user, 0) + inputLine.price
|
||||
)
|
||||
else:
|
||||
database.personNegTransactions[inputLine.user] = (
|
||||
database.personNegTransactions.setdefault(inputLine.user, 0) + inputLin |