Back to Python 3.6 for the sake of Debian packaging (initial test!)

This commit is contained in:
s3lph 2018-11-03 18:28:28 +01:00
parent 960a1c8517
commit ea57485cfb
12 changed files with 148 additions and 86 deletions

View file

@ -1,5 +1,5 @@
--- ---
image: s3lph/matemat-ci:20180802-02 image: s3lph/matemat-ci:20181103-01
stages: stages:
- test - test
@ -17,9 +17,9 @@ test:
stage: test stage: test
script: script:
- pip3 install -e . - pip3 install -e .
- sudo -u matemat python3.7 -m coverage run --rcfile=setup.cfg -m unittest discover matemat - sudo -u matemat python3.6 -m coverage run --rcfile=setup.cfg -m unittest discover matemat
- sudo -u matemat python3.7 -m coverage combine - sudo -u matemat python3.6 -m coverage combine
- sudo -u matemat python3.7 -m coverage report --rcfile=setup.cfg - sudo -u matemat python3.6 -m coverage report --rcfile=setup.cfg
codestyle: codestyle:
stage: test stage: test
@ -45,8 +45,8 @@ build_docker:
build_wheel: build_wheel:
stage: build stage: build
script: script:
- python3.7 setup.py egg_info --tag-build "+$CI_COMMIT_SHA" --tag-date bdist_wheel - python3.6 setup.py egg_info --tag-build "+$CI_COMMIT_SHA" --tag-date bdist_wheel
- python3.7 setup.py egg_info --tag-build "+$CI_COMMIT_REF_NAME" --tag-date bdist_wheel - python3.6 setup.py egg_info --tag-build "+$CI_COMMIT_REF_NAME" --tag-date bdist_wheel
artifacts: artifacts:
paths: paths:
- "dist/*.whl" - "dist/*.whl"
@ -56,29 +56,28 @@ build_wheel:
- master - master
- tags - tags
# This is only useful once Debian either defaults python3 to 3.7 or provides python3.7- packages build_deb:
# for the needed dependencies... But it SHOULD work... stage: build
#build_deb: script:
# stage: build - cp -r static/ package/debian/matemat/usr/lib/matemat/static/
# script: - cp -r templates/ package/debian/matemat/usr/lib/matemat/templates/
# - cp -r static/ package/debian/matemat/usr/lib/matemat/static/ - python3.6 setup.py egg_info -d -b +master install --root=package/debian/matemat/ --prefix=/usr --optimize=1
# - cp -r templates/ package/debian/matemat/usr/lib/matemat/templates/ - export PYTHON_BIN=$(which python3)
# - python3.7 setup.py egg_info -d -b +master install --root=package/debian/matemat/ --prefix=/usr --optimize=1 - cd package/debian
# - export PYTHON_BIN=$(which python3) - mv matemat/usr/lib/python3.6/{site,dist}-packages
# - cd package/debian - mv matemat/usr/bin/matemat matemat/usr/lib/matemat/matemat
# - mv matemat/usr/lib/python3.7/{site,dist}-packages - rm -rf matemat/usr/bin
# - mv matemat/usr/bin/matemat matemat/usr/lib/matemat/matemat - find . -type f -exec sed -re "s#${PYTHON_BIN}#/usr/bin/python3.6#g" -i {} \;
# - rm -rf matemat/usr/bin - dpkg-deb --build matemat
# - find . -type f -exec sed -re "s#${PYTHON_BIN}#/usr/bin/python3.7#g" -i {} \; - mv matemat.deb "matemat_${MATEMAT_VERSION}+${CI_COMMIT_REF_NAME}-1_all.deb"
# - dpkg-deb --build matemat artifacts:
# - mv matemat.deb "matemat_${MATEMAT_VERSION}+${CI_COMMIT_REF_NAME}-1_all.deb" paths:
# artifacts: - "package/debian/*.deb"
# paths: only:
# - "package/debian/*.deb" - 27-deployment
# only: - staging
# - staging - master
# - master - tags
# - tags
build_archlinux: build_archlinux:
stage: build stage: build
@ -99,7 +98,7 @@ build_archlinux:
paths: paths:
- "package/archlinux/*.pkg.tar.xz" - "package/archlinux/*.pkg.tar.xz"
only: only:
- deployment - 27-deployment
- staging - staging
- master - master
- tags - tags

View file

@ -16,7 +16,7 @@ This project intends to provide a well-tested and maintainable alternative to
## Dependencies ## Dependencies
- Python 3 (>=3.7) - Python 3 (>=3.6)
- Python dependencies: - Python dependencies:
- file-magic - file-magic
- jinja2 - jinja2

View file

@ -1,5 +1,4 @@
from __future__ import annotations
from typing import Any, Dict, List, Optional, Tuple, Type from typing import Any, Dict, List, Optional, Tuple, Type
import crypt import crypt
@ -39,7 +38,7 @@ class MatematDatabase(object):
""" """
self.db: DatabaseWrapper = DatabaseWrapper(filename) self.db: DatabaseWrapper = DatabaseWrapper(filename)
def __enter__(self) -> MatematDatabase: def __enter__(self) -> 'MatematDatabase':
# Pass context manager stuff through to the database wrapper # Pass context manager stuff through to the database wrapper
self.db.__enter__() self.db.__enter__()
return self return self

View file

@ -1,22 +1,19 @@
from dataclasses import dataclass
@dataclass
class Product: class Product:
""" """
Representation of a product offered by the Matemat, with a name, prices for users, and the number of items Representation of a product offered by the Matemat, with a name, prices for users, and the number of items
currently in stock. currently in stock.
:param id: The product ID in the database. :param _id: The product ID in the database.
:param name: The product's name. :param name: The product's name.
:param price_member: The price of a unit of this product for users marked as "members". :param price_member: The price of a unit of this product for users marked as "members".
:param price_non_member: The price of a unit of this product for users NOT marked as "members". :param price_non_member: The price of a unit of this product for users NOT marked as "members".
:param stock: The number of items of this product currently in stock. :param stock: The number of items of this product currently in stock.
""" """
id: int def __init__(self, _id: int, name: str, price_member: int, price_non_member: int, stock: int) -> None:
name: str self.id: int = _id
price_member: int self.name: str = name
price_non_member: int self.price_member: int = price_member
stock: int self.price_non_member: int = price_non_member
self.stock: int = stock

View file

@ -1,17 +1,30 @@
from typing import List from typing import List
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from matemat.db.primitives import User, Transaction from matemat.db.primitives import User, Transaction
@dataclass
class Receipt: class Receipt:
"""
Representation of a receipt for a user and a given timespan.
id: int :param _id: The receipt ID in the database.
transactions: List[Transaction] :param transactions: The list of transactions on this receipt.
user: User :param user: The user for whom this receipt was issued.
from_date: datetime :param from_date: The beginning of the time span this receipt covers.
to_date: datetime :param to_date: The end of the time span this receipt covers.
"""
def __init__(self,
_id: int,
transactions: List[Transaction],
user: User,
from_date: datetime,
to_date: datetime) -> None:
self.id: int = _id
self.transactions: List[Transaction] = transactions
self.user: User = user
self.from_date: datetime = from_date
self.to_date: datetime = to_date

View file

@ -11,7 +11,7 @@ class ReceiptPreference(Enum):
A user's preference for the frequency of receiving receipts. A user's preference for the frequency of receiving receipts.
""" """
def __new__(cls, *args, **kwargs): def __new__(cls, *args, **kwargs) -> 'ReceiptPreference':
e = object.__new__(cls) e = object.__new__(cls)
# The enum's internal value # The enum's internal value
e._value_: int = args[0] e._value_: int = args[0]

View file

@ -1,6 +1,5 @@
from typing import Optional from typing import Optional
from dataclasses import dataclass
from datetime import datetime from datetime import datetime
@ -8,14 +7,23 @@ from matemat.db.primitives import User
from matemat.util.currency_format import format_chf from matemat.util.currency_format import format_chf
@dataclass(frozen=True)
class Transaction: class Transaction:
"""
Representation of a generic transaction involving an user and an amount of money.
id: int :param _id: The transaction ID in the database.
user: User :param user: The user affected by this transaction.
value: int :param value: The monetary value of this transaction.
old_balance: int :param old_balance: The balance on the user's account before this transaction.
date: datetime :param date: The date of this transaction.
"""
def __init__(self, _id: int, user: User, value: int, old_balance: int, date: datetime) -> None:
self.id: int = _id
self.user: User = user
self.value: int = value
self.old_balance: int = old_balance
self.date: datetime = date
@property @property
def receipt_date(self) -> str: def receipt_date(self) -> str:
@ -38,29 +46,71 @@ class Transaction:
return None return None
@dataclass(frozen=True)
class Consumption(Transaction): class Consumption(Transaction):
"""
Representation of a consumption involving an user, a product and an amount of money.
product: str :param _id: The transaction ID in the database.
:param user: The user affected by this transaction.
:param value: The (negative) price of the product that was bought.
:param old_balance: The balance on the user's account before this transaction.
:param date: The date of this transaction.
:param product: The name of the product that was bought.
"""
def __init__(self, _id: int, user: User, value: int, old_balance: int, date: datetime, product: str) -> None:
super().__init__(_id, user, value, old_balance, date)
self.product: str = product
@property @property
def receipt_description(self) -> str: def receipt_description(self) -> str:
return self.product return self.product
@dataclass(frozen=True)
class Deposit(Transaction): class Deposit(Transaction):
"""
Representation of a deposit involving an user and an amount of money.
:param _id: The transaction ID in the database.
:param user: The user affected by this transaction.
:param value: The amount of money that was deposited on the account.
:param old_balance: The balance on the user's account before this transaction.
:param date: The date of this transaction.
"""
def __init__(self, _id: int, user: User, value: int, old_balance: int, date: datetime) -> None:
super().__init__(_id, user, value, old_balance, date)
@property @property
def receipt_description(self) -> str: def receipt_description(self) -> str:
return 'Deposit' return 'Deposit'
@dataclass(frozen=True)
class Modification(Transaction): class Modification(Transaction):
"""
Representation of a administrative account balance modification. Involves the affected user, the agent that
performed the modification and optionally a message for the reason of the modification.
agent: str :param _id: The transaction ID in the database.
reason: Optional[str] :param user: The user affected by this transaction.
:param value: The amount of money that was deposited on the account.
:param old_balance: The balance on the user's account before this transaction.
:param date: The date of this transaction.
:param agent: The username of the agent performing the modification.
:param reason: The reason for this modification, as provided by the agent.
"""
def __init__(self,
_id: int,
user: User,
value: int,
old_balance: int,
date: datetime,
agent: str,
reason: Optional[str]) -> None:
super().__init__(_id, user, value, old_balance, date)
self.agent: str = agent
self.reason: Optional[str] = reason
@property @property
def receipt_description(self) -> str: def receipt_description(self) -> str:

View file

@ -1,30 +1,36 @@
from typing import Optional from typing import Optional
from dataclasses import dataclass
from matemat.db.primitives.ReceiptPreference import ReceiptPreference from matemat.db.primitives.ReceiptPreference import ReceiptPreference
@dataclass
class User: class User:
""" """
Representation of a user registered with the Matemat, with a name, e-mail address (optional), whether the user is a Representation of a user registered with the Matemat, with a name, e-mail address (optional), whether the user is a
member of the organization the Matemat instance is used in, whether the user is an administrator, and the user's member of the organization the Matemat instance is used in, whether the user is an administrator, and the user's
account balance. account balance.
:param id: The user ID in the database. :param _id: The user ID in the database.
:param username: The user's name. :param name: The user's name.
:param balance: The balance of the user's account. :param balance: The balance of the user's account.
:param email: The user's e-mail address (optional). :param email: The user's e-mail address (optional).
:param admin: Whether the user is an administrator. :param is_admin: Whether the user is an administrator.
:param member: Whether the user is a member. :param is_member: Whether the user is a member.
:param receipt_pref: The user's preference on how often to receive transaction receipts. :param receipt_pref: The user's preference on how often to receive transaction receipts.
""" """
id: int def __init__(self,
name: str _id: int,
balance: int name: str,
email: Optional[str] = None balance: int,
is_admin: bool = False email: Optional[str] = None,
is_member: bool = False is_admin: bool = False,
receipt_pref: ReceiptPreference = ReceiptPreference.NONE is_member: bool = False,
receipt_pref: ReceiptPreference = ReceiptPreference.NONE) -> None:
self.id: int = _id
self.name: str = name
self.balance: int = balance
self.email: Optional[str] = email
self.is_admin: bool = is_admin
self.is_member: bool = is_member
self.receipt_pref: ReceiptPreference = receipt_pref

View file

@ -1,5 +1,4 @@
from __future__ import annotations
from typing import Any, Optional from typing import Any, Optional
import sqlite3 import sqlite3
@ -49,7 +48,7 @@ class DatabaseWrapper(object):
self._filename: str = filename self._filename: str = filename
self._sqlite_db: Optional[sqlite3.Connection] = None self._sqlite_db: Optional[sqlite3.Connection] = None
def __enter__(self) -> DatabaseWrapper: def __enter__(self) -> 'DatabaseWrapper':
self.connect() self.connect()
return self return self

View file

@ -1,5 +1,4 @@
from __future__ import annotations
from typing import Dict, Iterator, List, Tuple, Union from typing import Dict, Iterator, List, Tuple, Union
@ -25,7 +24,7 @@ class RequestArguments(object):
""" """
self.__container: Dict[str, RequestArgument] = dict() self.__container: Dict[str, RequestArgument] = dict()
def __getitem__(self, key: str) -> RequestArgument: def __getitem__(self, key: str) -> 'RequestArgument':
""" """
Retrieve the argument for the given name, creating it on the fly, if it doesn't exist. Retrieve the argument for the given name, creating it on the fly, if it doesn't exist.
@ -41,7 +40,7 @@ class RequestArguments(object):
# Return the argument for the name # Return the argument for the name
return self.__container[key] return self.__container[key]
def __getattr__(self, key: str) -> RequestArgument: def __getattr__(self, key: str) -> 'RequestArgument':
""" """
Syntactic sugar for accessing values with a name that can be used in Python attributes. The value will be Syntactic sugar for accessing values with a name that can be used in Python attributes. The value will be
returned as an immutable view. returned as an immutable view.
@ -279,7 +278,7 @@ class RequestArgument(object):
# Yield an immutable scalar view for each (ctype, value) element in the array # Yield an immutable scalar view for each (ctype, value) element in the array
yield _View(self.__name, v) yield _View(self.__name, v)
def __getitem__(self, index: Union[int, slice]) -> RequestArgument: def __getitem__(self, index: Union[int, slice]) -> 'RequestArgument':
""" """
Index the argument with either an int or a slice. The returned values are represented as immutable Index the argument with either an int or a slice. The returned values are represented as immutable
RequestArgument views. RequestArgument views.

View file

@ -17,7 +17,7 @@ soda machine's touch screen).
''', ''',
packages=find_packages(exclude=['*.test']), packages=find_packages(exclude=['*.test']),
python_requires='>=3.7', python_requires='>=3.6',
install_requires=[ install_requires=[
'file-magic', 'file-magic',
'jinja2', 'jinja2',

View file

@ -1,6 +1,6 @@
# There is no buster image yet and stretch doesn't have a docker package. So let's just "upgrade" the image to buster. # There is no buster image yet and stretch doesn't have a docker package. So let's just "upgrade" the image to buster.
FROM python:3.7-stretch FROM python:3.6-stretch
RUN sed -re 's/stretch/buster/g' -i /etc/apt/sources.list \ RUN sed -re 's/stretch/buster/g' -i /etc/apt/sources.list \
&& useradd -d /home/matemat -m matemat \ && useradd -d /home/matemat -m matemat \
@ -9,7 +9,7 @@ RUN sed -re 's/stretch/buster/g' -i /etc/apt/sources.list \
&& chown matemat:matemat -R /var/matemat/upload \ && chown matemat:matemat -R /var/matemat/upload \
&& apt-get update -qy \ && apt-get update -qy \
&& apt-get install -y --no-install-recommends file sudo openssh-client git docker.io build-essential \ && apt-get install -y --no-install-recommends file sudo openssh-client git docker.io build-essential \
&& python3.7 -m pip install coverage wheel pycodestyle mypy \ && python3.6 -m pip install coverage wheel pycodestyle mypy \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
WORKDIR /home/matemat WORKDIR /home/matemat