Back to Python 3.6 for the sake of Debian packaging (initial test!)
This commit is contained in:
parent
960a1c8517
commit
ea57485cfb
12 changed files with 148 additions and 86 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
2
setup.py
2
setup.py
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue