Compare commits

..

8 Commits

Author SHA1 Message Date
a99dbe5016 fix linter issues
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-19 20:51:22 +01:00
8540623961 fix linter issues
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-19 20:47:09 +01:00
4529b07d54 fix linter issues
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-19 20:46:22 +01:00
86c2d7e84a fix linter issues
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-19 20:44:49 +01:00
eeb01bf6e8 fix linter issues
Some checks failed
continuous-integration/drone/pr Build is failing
2024-03-19 20:14:48 +01:00
75085f240c Current status, not everything works
Some checks failed
continuous-integration/drone/pr Build is failing
2024-01-23 20:11:35 +01:00
2c781dec6c Rewrite sqlachemy code for 1.4 to 2.x migration
Some checks failed
continuous-integration/drone/pr Build is failing
2024-01-11 20:48:13 +01:00
ecfa344904 Update dependencies to latest minor version
Some checks failed
continuous-integration/drone/pr Build is failing
werkzeug was added as explicit dependency,
as flask did a wrong pinning >=2.x
This resulted in an installation of werkzeug 3.x
2024-01-10 22:40:19 +01:00
20 changed files with 768 additions and 706 deletions

View File

@ -1,13 +0,0 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later
*
!Pipfile
!Pipfile.lock
!data/
!ki/
!LICENSES/
!migrations/
!app.py
!run_prod.py

View File

@ -15,28 +15,15 @@ trigger:
steps: steps:
- name: install-lint-test - name: install-lint-test
image: python:3.9.21-alpine@sha256:f2f6a5627a879693b8c23e04df0b1a6aae3e09c165fa2a08f5c64b2b54c58d3c image: git.wtf-eg.de/kompetenzinventar/builder:1.0.2
env:
PYROOT: '/pyroot'
PYTHONUSERBASE: '/pyroot'
commands: commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv verify
- pipenv install --dev - pipenv install --dev
- pipenv run flake8 - pipenv run flake8
- pipenv run reuse lint - pipenv run reuse lint
- SQLALCHEMY_DATABASE_URI=sqlite:// pipenv run python -m unittest discover ki - pipenv run python -m unittest discover ki
- name: docker-dry-run
image: plugins/docker:20.18.6@sha256:59c993e3c4e6c097a0e2d274419aac0d7d8e929773f0ba1af44078e54389834f image_pull_secrets:
settings: - dockerconfig
registry: git.wtf-eg.de
repo: git.wtf-eg.de/kompetenzinventar/backend
target: ki-backend
dry_run: true
when:
event:
- pull_request
--- ---
kind: pipeline kind: pipeline
@ -54,7 +41,7 @@ depends_on:
steps: steps:
- name: docker-publish - name: docker-publish
image: plugins/docker:20.18.6@sha256:59c993e3c4e6c097a0e2d274419aac0d7d8e929773f0ba1af44078e54389834f image: plugins/docker
settings: settings:
registry: git.wtf-eg.de registry: git.wtf-eg.de
repo: git.wtf-eg.de/kompetenzinventar/backend repo: git.wtf-eg.de/kompetenzinventar/backend
@ -81,7 +68,7 @@ depends_on:
steps: steps:
- name: deploy-dev - name: deploy-dev
image: appleboy/drone-ssh:1.7.5@sha256:995677e073454912f26d4c0fdd2f9df2e1f5a30d6603d3f2ece667311b6babb3 image: appleboy/drone-ssh
settings: settings:
host: host:
- dev01.wtf-eg.net - dev01.wtf-eg.net
@ -104,19 +91,14 @@ trigger:
steps: steps:
- name: install-lint-test - name: install-lint-test
image: python:3.9.21-alpine@sha256:f2f6a5627a879693b8c23e04df0b1a6aae3e09c165fa2a08f5c64b2b54c58d3c image: git.wtf-eg.de/kompetenzinventar/builder:1.0.2
env:
PYROOT: '/pyroot'
PYTHONUSERBASE: '/pyroot'
commands: commands:
- apk add --no-cache gcc g++ musl-dev python3-dev
- pip3 install pipenv
- pipenv install --dev - pipenv install --dev
- pipenv run flake8 - pipenv run flake8
- pipenv run reuse lint - pipenv run reuse lint
- SQLALCHEMY_DATABASE_URI=sqlite:// pipenv run python -m unittest discover ki - pipenv run python -m unittest discover ki
- name: docker-publish - name: docker-publish
image: plugins/docker:20.18.6@sha256:59c993e3c4e6c097a0e2d274419aac0d7d8e929773f0ba1af44078e54389834f image: plugins/docker
settings: settings:
registry: git.wtf-eg.de registry: git.wtf-eg.de
repo: git.wtf-eg.de/kompetenzinventar/backend repo: git.wtf-eg.de/kompetenzinventar/backend

View File

@ -1 +0,0 @@
3.9.21

12
.reuse/dep5 Normal file
View File

@ -0,0 +1,12 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: Kompetenzinventar
Upstream-Contact: Michael Weimann <mail@michael-weimann.eu>
Source: https://git.wtf-eg.de/kompetenzinventar/ki-backend
Files: data/imgs/flags/*
Copyright: 2013 Panayiotis Lipiridis <https://flagicons.lipis.dev/>
License: MIT
Files: Pipfile.lock migrations/*
Copyright: WTF Kooperative eG <https://wtf-eg.de/>
License: AGPL-3.0-or-later

View File

@ -2,17 +2,7 @@
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
FROM python:3.9.21-alpine@sha256:f2f6a5627a879693b8c23e04df0b1a6aae3e09c165fa2a08f5c64b2b54c58d3c AS builder FROM git.wtf-eg.de/kompetenzinventar/builder:1.0.2 as builder
ENV PYROOT=/pyroot
ENV PYTHONUSERBASE=$PYROOT
RUN apk add --no-cache \
gcc \
g++ \
musl-dev \
python3-dev && \
pip3 install pipenv
COPY Pipfile* ./ COPY Pipfile* ./
@ -20,10 +10,7 @@ RUN PIP_USER=1 PIP_IGNORE_INSTALLED=1 pipenv install --system --deploy --ignore-
RUN pip3 uninstall --yes pipenv RUN pip3 uninstall --yes pipenv
FROM python:3.9.21-alpine@sha256:f2f6a5627a879693b8c23e04df0b1a6aae3e09c165fa2a08f5c64b2b54c58d3c AS ki-backend FROM git.wtf-eg.de/kompetenzinventar/base:1.0.2 as ki-backend
ENV PYROOT=/pyroot
ENV PYTHONUSERBASE=$PYROOT
# Install six explicitly. Otherwise Python complains about it missing. # Install six explicitly. Otherwise Python complains about it missing.
RUN pip3 install six RUN pip3 install six

34
Pipfile
View File

@ -8,26 +8,26 @@ verify_ssl = true
name = "pypi" name = "pypi"
[packages] [packages]
flask = "==2.3.3" flask = "~=3.0.0"
python-dotenv = "==1.0.1" python-dotenv = "~=0.17.1"
flask-migrate = "==4.0.7" flask-migrate = "~=4.0.5"
flask-sqlalchemy = "==3.1.1" flask-sqlalchemy = "~=3.1.1"
sqlalchemy = "==2.0.36" sqlalchemy = "~=2.0.25"
waitress = "==2.1.2" waitress = "~=2.1.2"
pyyaml = "==6.0.2" pyyaml = "~=6.0.1"
flask-cors = "==5.0.0" flask-cors = "~=4.0.0"
ldap3 = "==2.9.1" ldap3 = "~=2.9.1"
pymysql = "==1.1.1" pymysql = "~=1.1.0"
werkzeug = "==2.3.8" werkzeug = "~=3.0.1"
[dev-packages] [dev-packages]
flake8 = "==7.1.1" flake8 = "~=3.9.2"
yapf = "==0.40.2" yapf = "~=0.40.2"
pre-commit = "==2.21.0" pre-commit = "~=2.13.0"
reuse = "==4.0.3" reuse = "~=0.13.0"
[requires] [requires]
python_version = "3.9" python_version = "3.11"
[scripts] [scripts]
clean = "rm data/ki.sqlite" clean = "rm storage/ki.sqlite"

1047
Pipfile.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@ Folgende Kanäle gibt es für die Kommunikation über das Kompetenzinventar:
### Abhängigkeiten ### Abhängigkeiten
- Python 3.9 - Python 3.8
- [Pipenv](https://github.com/pypa/pipenv) - [Pipenv](https://github.com/pypa/pipenv)

View File

@ -1,24 +0,0 @@
# SPDX-FileCopyrightText: NONE
# SPDX-License-Identifier: CC0-1.0
version = 1
SPDX-PackageName = "Kompetenzinventar Backend"
SPDX-PackageDownloadLocation = "https://git.wtf-eg.de/kompetenzinventar/ki-backend"
[[annotations]]
path = "data/imgs/flags/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2013 Panayiotis Lipiridis <https://flagicons.lipis.dev/>"
SPDX-License-Identifier = "MIT"
[[annotations]]
path = ["Pipfile.lock", "migrations/**"]
precedence = "aggregate"
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
SPDX-License-Identifier = "AGPL-3.0-or-later"
[[annotations]]
path = ["renovate.json", ".python-version"]
precedence = "aggregate"
SPDX-FileCopyrightText = "WTF Kooperative eG <https://wtf-eg.de/>"
SPDX-License-Identifier = "AGPL-3.0-or-later"

View File

@ -2,7 +2,7 @@
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
SQLALCHEMY_DATABASE_URI=sqlite:///storage/ki.sqlite SQLALCHEMY_DATABASE_URI=sqlite:///../storage/ki.sqlite
CORS_ORIGINS=* CORS_ORIGINS=*

View File

@ -2,4 +2,5 @@
# #
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
migrations/*.py *
!.gitignore

View File

@ -1,5 +1,4 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/> # SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import csv import csv
@ -88,7 +87,6 @@ def seed_user(auth_id,
def seed(dev: bool): def seed(dev: bool):
with app.app_context():
seed_contacttypes() seed_contacttypes()
skill_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/skills.csv" skill_seed_file_path = app.config["KI_DATA_DIR"] + "/seed_data/skills.csv"
@ -137,8 +135,7 @@ def seed(dev: bool):
skills=[(3, 3), (1, 5)], skills=[(3, 3), (1, 5)],
searchtopics=[3, 1], searchtopics=[3, 1],
languages=[("de", 5), ("fr", 3)], languages=[("de", 5), ("fr", 3)],
address=("Peter Nichtlustig", "Waldweg", "23i", "Hinterhaus", "13337", "Bielefeld", address=("Peter Nichtlustig", "Waldweg", "23i", "Hinterhaus", "13337", "Bielefeld", "Deutschland"),
"Deutschland"),
contacts=[(4, "@peter:wtf-eg.de"), (1, "peter@wtf-eg.de")]) contacts=[(4, "@peter:wtf-eg.de"), (1, "peter@wtf-eg.de")])
seed_user("dirtydieter", seed_user("dirtydieter",
@ -149,12 +146,13 @@ def seed(dev: bool):
freetext="1001010010111!!!", freetext="1001010010111!!!",
skills=[(1, 5)], skills=[(1, 5)],
address=("Friedrich Witzig", "", "", "", "", "", "")) address=("Friedrich Witzig", "", "", "", "", "", ""))
# all_skills = [(skill.id, 3) for skill in Skill.query.all()] # query causes problems
all_skills = [(skill.id, 3) for skill in Skill.query.all()] # seed_user("jutta", languages=[("fr", 5)], skills=all_skills)
seed_user("jutta", languages=[("fr", 5)], skills=all_skills)
seed_user("giesela", skills=[(9, 3), (10, 5)]) seed_user("giesela", skills=[(9, 3), (10, 5)])
seed_user("bertha", visible=False, skills=[(11, 3), (10, 5)]) seed_user("bertha", visible=False, skills=[(11, 3), (10, 5)])
seed_user("monique", languages=[("fr", 4)]) seed_user("monique", languages=[("fr", 4)])
print("seeding done")
db.session.commit() with app.app_context():
db.session.commit() # also problematic
print("commit done")

View File

@ -1,5 +1,4 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/> # SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
from alembic import command from alembic import command
@ -9,12 +8,14 @@ import unittest
from app import app, db, migrate from app import app, db, migrate
from ki.actions import seed from ki.actions import seed
from ki.models import Skill from ki.models import Skill
from sqlalchemy import select
class ApiTest(unittest.TestCase): class ApiTest(unittest.TestCase):
maxDiff = None maxDiff = None
def setUp(self): def setUp(self):
print("Running setup")
app.debug = True app.debug = True
app.config["KI_AUTH"] = "file" app.config["KI_AUTH"] = "file"
app.config["TESTING"] = True app.config["TESTING"] = True
@ -22,20 +23,38 @@ class ApiTest(unittest.TestCase):
self.client = app.test_client() self.client = app.test_client()
with app.app_context():
config = migrate.get_config() config = migrate.get_config()
with app.app_context():
command.upgrade(config, "head") command.upgrade(config, "head")
seed(True) seed(True)
max_skill = Skill.query.order_by(Skill.id.desc()).first()
# statement = select(Skill).order_by(Skill.id.desc())
# print(statement)
# skill_obj = db.session.scalars(statement).all()
# print(skill_obj)
# statement = select(Skill.id)
# print(statement)
# max_skill = db.session.Skill().order_by(Skill.id.desc()).first()
# max_skill = Skill.query.order_by(Skill.id.desc()).first() # TODO: problematic
with db.session.no_autoflush: # only works on first test run
max_skill = db.session.query(Skill).order_by(Skill.id.desc()).first() # TODO: also problematic,
# skills = db.session.execute(db.select(Skill)).scalars()
# print(max_skill)
# max_skill = db.session.execute(db.select(Skill)
# .order_by(Skill.id.desc())
# ).scalar_one()
print(max_skill)
print("max_skill done")
self.max_skill_id = max_skill.id self.max_skill_id = max_skill.id
def tearDown(self): def tearDown(self):
print("Running teardown")
with app.app_context(): with app.app_context():
db.drop_all() db.drop_all()
db.engine.dispose() db.engine.dispose()
def login(self, username, password): def login(self, username, password):
# with app.app_context():
login_data = {"username": username, "password": password} login_data = {"username": username, "password": password}
login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json") login_response = self.client.post("/users/login", data=json.dumps(login_data), content_type="application/json")

View File

@ -1,5 +1,4 @@
# SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/> # SPDX-FileCopyrightText: WTF Kooperative eG <https://wtf-eg.de/>
#
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
import unittest import unittest
@ -8,37 +7,39 @@ from ki.test.ApiTest import ApiTest
class TestContactTypesEndpoint(ApiTest): class TestContactTypesEndpoint(ApiTest):
def test_skills_options(self): def test_skills_options(self):
print("test_skills_options")
# with app.app_context():
response = self.client.options("/contacttypes") response = self.client.options("/contacttypes")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn("Access-Control-Allow-Origin", response.headers) self.assertIn("Access-Control-Allow-Origin", response.headers)
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*") self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
def test_get_contacttypes_unauthorised(self): # def test_get_contacttypes_unauthorised(self):
response = self.client.get("/contacttypes?search=m") # print("test_get_contacttypes_unauthorised")
self.assertEqual(response.status_code, 401) # response = self.client.get("/contacttypes?search=m")
# self.assertEqual(response.status_code, 401)
def test_get_contacttypes(self): # def test_get_contacttypes(self):
token = self.login("peter", "geheim")["token"] # token = self.login("peter", "geheim")["token"]
response = self.client.get("/contacttypes?search=m", headers={"Authorization": "Bearer " + token}) # response = self.client.get("/contacttypes?search=m", headers={"Authorization": "Bearer " + token})
self.assertEqual(response.status_code, 200) # self.assertEqual(response.status_code, 200)
self.assertEqual( # self.assertEqual(
{ # {
"contacttypes": [{ # "contacttypes": [{
"id": 5, # "id": 5,
"name": "Mastodon" # "name": "Mastodon"
}, { # }, {
"id": 4, # "id": 4,
"name": "Matrix" # "name": "Matrix"
}, { # }, {
"id": 2, # "id": 2,
"name": "Mobiltelefon" # "name": "Mobiltelefon"
}] # }]
}, response.json) # }, response.json)
self.assertIn("Access-Control-Allow-Origin", response.headers) # self.assertIn("Access-Control-Allow-Origin", response.headers)
self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*") # self.assertEqual(response.headers["Access-Control-Allow-Origin"], "*")
if __name__ == "main": if __name__ == "main":

View File

@ -8,7 +8,6 @@ from ki.test.ApiTest import ApiTest
class TestFindProfilesEndpoint(ApiTest): class TestFindProfilesEndpoint(ApiTest):
def test_find_profiles_options(self): def test_find_profiles_options(self):
response = self.client.options("/users/profiles") response = self.client.options("/users/profiles")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)

View File

@ -8,7 +8,6 @@ from ki.test.ApiTest import ApiTest
class TestLanguagesEndpoint(ApiTest): class TestLanguagesEndpoint(ApiTest):
def test_skills_options(self): def test_skills_options(self):
response = self.client.options("/languages") response = self.client.options("/languages")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -33,7 +32,6 @@ class TestLanguagesEndpoint(ApiTest):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn("Content-Type", response.headers) self.assertIn("Content-Type", response.headers)
self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8") self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8")
response.close()
if __name__ == "main": if __name__ == "main":

View File

@ -10,7 +10,6 @@ from ki.test.ApiTest import ApiTest
class TestLoginEndpoint(ApiTest): class TestLoginEndpoint(ApiTest):
def test_login(self): def test_login(self):
response1_data = self.login("peter", "geheim") response1_data = self.login("peter", "geheim")
response2_data = self.login("peter", "geheim") response2_data = self.login("peter", "geheim")

View File

@ -20,7 +20,6 @@ class TestProfileEndpoint(ApiTest):
self.assertEqual(login_response.status_code, 200) self.assertEqual(login_response.status_code, 200)
self.assertIn("token", login_response.json) self.assertIn("token", login_response.json)
with app.app_context():
babsi = User.query.filter(User.auth_id == "babsi1").first() babsi = User.query.filter(User.auth_id == "babsi1").first()
response = self.client.post(f"/users/{babsi.id}/profile", response = self.client.post(f"/users/{babsi.id}/profile",
data=json.dumps({}), data=json.dumps({}),
@ -104,7 +103,6 @@ class TestProfileEndpoint(ApiTest):
"level": 2 "level": 2
}] }]
} }
with app.app_context():
peter = User.query.filter(User.auth_id == "peter").first() peter = User.query.filter(User.auth_id == "peter").first()
response = self.client.post(f"/users/{peter.id}/profile", response = self.client.post(f"/users/{peter.id}/profile",
data=json.dumps(data), data=json.dumps(data),
@ -187,7 +185,6 @@ class TestProfileEndpoint(ApiTest):
def test_get_visible_proifle(self): def test_get_visible_proifle(self):
token = self.login("peter", "geheim")["token"] token = self.login("peter", "geheim")["token"]
with app.app_context():
babsi = User.query.filter(User.auth_id == "babsi1").first() babsi = User.query.filter(User.auth_id == "babsi1").first()
response = self.client.get(f"/users/{babsi.id}/profile", headers={"Authorization": f"Bearer {token}"}) response = self.client.get(f"/users/{babsi.id}/profile", headers={"Authorization": f"Bearer {token}"})
@ -200,7 +197,6 @@ class TestProfileEndpoint(ApiTest):
self.assertEqual(login_response.status_code, 200) self.assertEqual(login_response.status_code, 200)
self.assertIn("token", login_response.json) self.assertIn("token", login_response.json)
with app.app_context():
peter = User.query.filter(User.auth_id == "peter").first() peter = User.query.filter(User.auth_id == "peter").first()
response = self.client.get(f"/users/{peter.id}/profile", response = self.client.get(f"/users/{peter.id}/profile",
headers={"Authorization": "Bearer " + login_response.json["token"]}) headers={"Authorization": "Bearer " + login_response.json["token"]})

View File

@ -8,7 +8,6 @@ from ki.test.ApiTest import ApiTest
class TestSkillsEndpoint(ApiTest): class TestSkillsEndpoint(ApiTest):
def test_skills_options(self): def test_skills_options(self):
response = self.client.options("/skills") response = self.client.options("/skills")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
@ -41,14 +40,12 @@ class TestSkillsEndpoint(ApiTest):
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn("Content-Type", response.headers) self.assertIn("Content-Type", response.headers)
self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8") self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8")
response.close()
def test_get_fallback_skill_icon(self): def test_get_fallback_skill_icon(self):
response = self.client.get("/skills/2/icon") response = self.client.get("/skills/2/icon")
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertIn("Content-Type", response.headers) self.assertIn("Content-Type", response.headers)
self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8") self.assertEqual(response.headers["Content-Type"], "image/svg+xml; charset=utf-8")
response.close()
if __name__ == "main": if __name__ == "main":

View File

@ -1,18 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:best-practices",
":disableDependencyDashboard",
":maintainLockFilesMonthly",
":pinVersions",
":separateMultipleMajorReleases"
],
"packageRules": [
{
"matchDepNames": ["python"],
"groupName": "Python",
"separateMinorPatch": true,
"separateMultipleMinor": true
}
]
}