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

View file

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

View file

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

View file

@ -1,22 +1,19 @@
from dataclasses import dataclass
@dataclass
class Product:
"""
Representation of a product offered by the Matemat, with a name, prices for users, and the number of items
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 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 stock: The number of items of this product currently in stock.
"""
id: int
name: str
price_member: int
price_non_member: int
stock: int
def __init__(self, _id: int, name: str, price_member: int, price_non_member: int, stock: int) -> None:
self.id: int = _id
self.name: str = name
self.price_member: int = price_member
self.price_non_member: int = price_non_member
self.stock: int = stock

View file

@ -1,17 +1,30 @@
from typing import List
from dataclasses import dataclass
from datetime import datetime
from matemat.db.primitives import User, Transaction
@dataclass
class Receipt:
"""
Representation of a receipt for a user and a given timespan.
id: int
transactions: List[Transaction]
user: User
from_date: datetime
to_date: datetime
:param _id: The receipt ID in the database.
:param transactions: The list of transactions on this receipt.
:param user: The user for whom this receipt was issued.
:param from_date: The beginning of the time span this receipt covers.
: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.
"""
def __new__(cls, *args, **kwargs):
def __new__(cls, *args, **kwargs) -> 'ReceiptPreference':
e = object.__new__(cls)
# The enum's internal value
e._value_: int = args[0]

View file

@ -1,6 +1,5 @@
from typing import Optional
from dataclasses import dataclass
from datetime import datetime
@ -8,14 +7,23 @@ from matemat.db.primitives import User
from matemat.util.currency_format import format_chf
@dataclass(frozen=True)
class Transaction:
"""
Representation of a generic transaction involving an user and an amount of money.
id: int
user: User
value: int
old_balance: int
date: datetime
:param _id: The transaction ID in the database.
:param user: The user affected by this transaction.
:param value: The monetary value of this transaction.
: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:
self.id: int = _id
self.user: User = user
self.value: int = value
self.old_balance: int = old_balance
self.date: datetime = date
@property
def receipt_date(self) -> str:
@ -38,29 +46,71 @@ class Transaction:
return None
@dataclass(frozen=True)
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
def receipt_description(self) -> str:
return self.product
@dataclass(frozen=True)
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
def receipt_description(self) -> str:
return 'Deposit'
@dataclass(frozen=True)
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
reason: Optional[str]
: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.
: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
def receipt_description(self) -> str:

View file

@ -1,30 +1,36 @@
from typing import Optional
from dataclasses import dataclass
from matemat.db.primitives.ReceiptPreference import ReceiptPreference
@dataclass
class User:
"""
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
account balance.
:param id: The user ID in the database.
:param username: The user's name.
:param _id: The user ID in the database.
:param name: The user's name.
:param balance: The balance of the user's account.
:param email: The user's e-mail address (optional).
:param admin: Whether the user is an administrator.
:param member: Whether the user is a member.
:param is_admin: Whether the user is an administrator.
:param is_member: Whether the user is a member.
:param receipt_pref: The user's preference on how often to receive transaction receipts.
"""
id: int
name: str
balance: int
email: Optional[str] = None
is_admin: bool = False
is_member: bool = False
receipt_pref: ReceiptPreference = ReceiptPreference.NONE
def __init__(self,
_id: int,
name: str,
balance: int,
email: Optional[str] = None,
is_admin: bool = False,
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
import sqlite3
@ -49,7 +48,7 @@ class DatabaseWrapper(object):
self._filename: str = filename
self._sqlite_db: Optional[sqlite3.Connection] = None
def __enter__(self) -> DatabaseWrapper:
def __enter__(self) -> 'DatabaseWrapper':
self.connect()
return self

View file

@ -1,5 +1,4 @@
from __future__ import annotations
from typing import Dict, Iterator, List, Tuple, Union
@ -25,7 +24,7 @@ class RequestArguments(object):
"""
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.
@ -41,7 +40,7 @@ class RequestArguments(object):
# Return the argument for the name
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
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 _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
RequestArgument views.

View file

@ -17,7 +17,7 @@ soda machine's touch screen).
''',
packages=find_packages(exclude=['*.test']),
python_requires='>=3.7',
python_requires='>=3.6',
install_requires=[
'file-magic',
'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.
FROM python:3.7-stretch
FROM python:3.6-stretch
RUN sed -re 's/stretch/buster/g' -i /etc/apt/sources.list \
&& 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 \
&& apt-get update -qy \
&& 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/*
WORKDIR /home/matemat