В данной документации показан пример использования keycloak в комбинации с wekan.
Данный пакет использует mongoengine как основу для моделей данных. У пакета есть расписанный гайд, он полноценный, но есть проблемы с примерами иногда. Однако вероятнее всего понадобится лишь информация о запросах к моделям.
Есть крутая фишка, готовые запросы - позволяет сохранить типичные запросы в виде отдельной функции.
Так как в питоновском PyPI пакета нет, нужно указать отдельный индекс (наш) для того чтоб скачать пакет.
!pip install footprint-mongoengine --extra-index https://footprint.auditory.ru/pypi/simple/
Looking in indexes: https://pypi.org/simple, https://footprint.auditory.ru/pypi/simple/
Collecting footprint-mongoengine
Downloading https://footprint.auditory.ru/pypi/packages/footprint_mongoengine-0.4.3-py3-none-any.whl (4.7 kB)
Collecting mongoengine
Downloading mongoengine-0.27.0-py3-none-any.whl (110 kB)
Collecting pymongo<5.0,>=3.4
Downloading pymongo-4.3.3-cp310-cp310-win_amd64.whl (382 kB)
Collecting dnspython<3.0.0,>=1.16.0
Downloading dnspython-2.3.0-py3-none-any.whl (283 kB)
Installing collected packages: dnspython, pymongo, mongoengine, footprint-mongoengine
Successfully installed dnspython-2.3.0 footprint-mongoengine-0.4.3 mongoengine-0.27.0 pymongo-4.3.3
WARNING: You are using pip version 21.1.2; however, version 23.0.1 is available.
You should consider upgrading via the 'C:\Users\Nikita\PycharmProjects\hse_imps_spring_2023\venv\Scripts\python.exe -m pip install --upgrade pip' command.
Сам пакет содержит в себе две функции и подпакеты (в которых и расположены модели). Функции нужны для подключения и отключения от базы данных. В целом можно воспользоваться и стандарными connect и disconnect из пакета mongonengine.
import footprint_mongoengine
help(footprint_mongoengine)
Help on package footprint_mongoengine:
NAME
footprint_mongoengine
PACKAGE CONTENTS
SUBMODULES
models
FUNCTIONS
connect(username: str, password: str, ip: str, port: int, db: str)
disconnect()
FILE
c:\users\nikita\pycharmprojects\hse_imps_spring_2023\venv\lib\site-packages\footprint_mongoengine\__init__.py
from footprint_mongoengine import connect, disconnect
connect('', '', '94.79.54.21', 27017, 'app')
Подпакет с моделями импортирует в себе файлы с моделями
from footprint_mongoengine import models
help(models)
Help on package footprint_mongoengine.models in footprint_mongoengine:
NAME
footprint_mongoengine.models
PACKAGE CONTENTS
competencies
nvr
photo
user
wekan
zulip
FILE
(built-in)
Пример импортирования набора моделей wekan и user
from footprint_mongoengine.models import wekan, user
Структура карточки wekan следующая. Основной класс в себе содержит информацию о карточке: статус, время последнего изменения, информацию и пользователей которые записаны в карточку. Здесь есть также два дополнительных класса, один перечисления для поля - какие могут быть статусы у карточки в сервисе, а второй класс - подмодель (по сути структура данных, которая будет является частью модели карточки). Стоит отметить что еще есть поле с ReferenceField. Данное поле ссылается на другие модели (а именно модель User, котороая содержит в себе информацию о пользователе).
class StatusEnum(Enum):
NEW = 'NEW'
IN_PROGRESS = 'IN_PROGRESS'
REVIEW = 'REVIEW'
COMPLETED = 'COMPLETED'
ARCHIVE = 'ARCHIVE'
UNKNOWN = 'UNKNOWN'
class CardInfo(EmbeddedDocument):
hours = IntField(required=True, default=0)
timestamp = DateTimeField(null=True)
assignees = ListField(StringField())
received_at = DateTimeField()
start_at = DateTimeField()
end_at = DateTimeField()
due_at = DateTimeField()
class Card(Document):
meta = {
'collection': 'wekan_cards'
}
users = ListField(ReferenceField(user.User))
board_id = StringField(required=True)
card_id = StringField(required=True)
info = EmbeddedDocumentField(CardInfo, required=True)
status = EnumField(StatusEnum, default=StatusEnum.UNKNOWN)
completed = BooleanField(required=True, default=False)
last_activity = DateTimeField(required=True)
Возьмем случайную карточку в пример, посмотрим какие данные внутри неё
example_card = wekan.Card.objects(id='63d1139d0ae9c35af0b20200')[0]
print(f"""
example_card
users : {example_card.users}
board_id : {example_card.board_id}
card_id : {example_card.card_id}
info : {example_card.info}
status : {example_card.status}
completed : {example_card.completed}
last_activity : {example_card.last_activity}
""")
example_card
users : [<User: User object>, <User: User object>]
board_id : 7n8XnvH7Pk89yczn5
card_id : nZPFPkQTthcNCrtFH
info : CardInfo object
status : StatusEnum.COMPLETED
completed : True
last_activity : 2021-12-21 09:16:37.742000
print(f"""
example_card.info
hours : {example_card.info.hours}
timestamp : {example_card.info.timestamp}
assignees : {example_card.info.assignees}
received_at : {example_card.info.received_at}
start_at : {example_card.info.start_at}
end_at : {example_card.info.end_at}
due_at : {example_card.info.due_at}
""")
example_card.info
hours : 4
timestamp : 2021-12-21 09:16:36.162000
assignees : ['jBiacHtujJmPgaQEu', 'PEqshGz47hamE4ibs']
received_at : None
start_at : None
end_at : None
due_at : None
print(f"""
example.users[0]
slug : {example_card.users[0].slug}
name : {example_card.users[0].name.firstname}
surname : {example_card.users[0].name.lastname}
""")
example.users[0]
slug : dakalinin
name : Денис
surname : Калинин
Возьмем пользователя из карточки и посмотрим другие его карточки
example_user = example_card.users[0]
for card in wekan.Card.objects(users=example_user):
print(f'card(card_id={card.card_id}, status={card.status}, '
f'completed={card.completed}, last_activity={card.last_activity})')
card(card_id=nZPFPkQTthcNCrtFH, status=StatusEnum.COMPLETED, completed=True, last_activity=2021-12-21 09:16:37.742000)
card(card_id=sBe7jfSv3jNdon4w8, status=StatusEnum.COMPLETED, completed=True, last_activity=2021-12-21 09:14:49.713000)
card(card_id=DbsoLLBBYjahaiRtt, status=StatusEnum.COMPLETED, completed=True, last_activity=2021-12-21 09:15:40.809000)
card(card_id=J9jFdXoXs2qyWzJH9, status=StatusEnum.COMPLETED, completed=True, last_activity=2021-12-21 09:14:56.965000)
card(card_id=ks3PXqcARkf3xwa3L, status=StatusEnum.COMPLETED, completed=True, last_activity=2022-01-28 06:59:46.492000)
card(card_id=L5HaiCMtaTmqBHZCd, status=StatusEnum.COMPLETED, completed=True, last_activity=2022-01-26 13:04:55.589000)
Получим пользователя используя модель из user и посмотрим его карточки
user_nikita = user.User.objects(slug='naklimin')[0]
print(f'user(slug={user_nikita.slug}, firstname={user_nikita.name.firstname}, '
f'lastname={user_nikita.name.lastname})')
user(slug=naklimin, firstname=Никита, lastname=Климин)
for card in wekan.Card.objects(users=user_nikita):
print(f'card(card_id={card.card_id}, status={card.status}, '
f'hours={card.info.hours}, last_activity={card.last_activity})')
card(card_id=qxjWZYNc8ePMgPoXM, status=StatusEnum.COMPLETED, hours=20, last_activity=2023-02-15 10:05:27.953000)
card(card_id=bzTYbozRdzB9Wtvos, status=StatusEnum.NEW, hours=0, last_activity=2023-02-12 10:59:00.401000)
card(card_id=gwSzomsy5a3ZHo4wE, status=StatusEnum.COMPLETED, hours=10, last_activity=2023-02-15 10:05:56.718000)
card(card_id=jwEezjR4tDN3opKGL, status=StatusEnum.COMPLETED, hours=20, last_activity=2023-01-19 15:30:44.103000)
card(card_id=rLWW5XjB6xmDuAmSh, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:50:21.459000)
card(card_id=k5thuzX6TyjwwbbqL, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:54:25.454000)
card(card_id=RY2v3rA2bXfkFwNvW, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:08:24.244000)
card(card_id=K2pFvw2rHXtwFioBM, status=StatusEnum.COMPLETED, hours=15, last_activity=2023-01-19 15:31:20.996000)
card(card_id=RkfftvF2AZ2dDyjWQ, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:09:21.997000)
card(card_id=uSRScYdhmabZ9ZEM8, status=StatusEnum.COMPLETED, hours=30, last_activity=2022-11-13 14:27:58.812000)
card(card_id=WtvLB2z6DSLeXdRS5, status=StatusEnum.COMPLETED, hours=9, last_activity=2022-11-13 14:25:35.786000)
card(card_id=ni7EJiR969Em5EKCf, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-13 14:23:51.091000)
card(card_id=y8TNjJy4SADMMWSNP, status=StatusEnum.ARCHIVE, hours=5, last_activity=2022-09-17 14:16:30.957000)
card(card_id=PQw6JLAjQx4HKJrNo, status=StatusEnum.ARCHIVE, hours=5, last_activity=2022-09-17 14:16:31.885000)
card(card_id=ds5uRMN74TgdFHyc2, status=StatusEnum.ARCHIVE, hours=10, last_activity=2022-09-17 14:16:32.902000)
card(card_id=mjfoBXjjM6mCuEpWm, status=StatusEnum.ARCHIVE, hours=15, last_activity=2022-09-17 14:16:37.791000)
card(card_id=jooNZwSJnfruAm8ge, status=StatusEnum.ARCHIVE, hours=10, last_activity=2022-09-17 14:16:39.032000)
card(card_id=2kLzzcmtB8efDj3uz, status=StatusEnum.ARCHIVE, hours=10, last_activity=2022-09-17 14:16:40.423000)
card(card_id=3CCXT2eYGuRvdXydF, status=StatusEnum.ARCHIVE, hours=20, last_activity=2022-09-17 14:16:50.984000)
card(card_id=do22jDazS5RJkXu3L, status=StatusEnum.ARCHIVE, hours=20, last_activity=2022-09-17 14:16:55.988000)
card(card_id=xbrEbLbbmAMrqXmGb, status=StatusEnum.ARCHIVE, hours=10, last_activity=2022-09-17 14:16:41.580000)
card(card_id=w93xXWPQk82As98Q4, status=StatusEnum.COMPLETED, hours=5, last_activity=2023-02-15 10:08:34.423000)
Усложним запрос
import datetime
for card in wekan.Card.objects(users=user_nikita,
status=wekan.StatusEnum.COMPLETED,
last_activity__lte=datetime.datetime(day=1, month=1, year=2023),
info__hours__gte=15,
):
print(f'card(card_id={card.card_id}, status={card.status}, '
f'hours={card.info.hours}, last_activity={card.last_activity})')
card(card_id=rLWW5XjB6xmDuAmSh, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:50:21.459000)
card(card_id=k5thuzX6TyjwwbbqL, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:54:25.454000)
card(card_id=RY2v3rA2bXfkFwNvW, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:08:24.244000)
card(card_id=RkfftvF2AZ2dDyjWQ, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:09:21.997000)
card(card_id=uSRScYdhmabZ9ZEM8, status=StatusEnum.COMPLETED, hours=30, last_activity=2022-11-13 14:27:58.812000)
card(card_id=ni7EJiR969Em5EKCf, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-13 14:23:51.091000)
Если нужны еще более сложные запросы, можно воспользоваться продвинутым форматом запросов
from mongoengine.queryset.visitor import Q
for card in wekan.Card.objects(
Q(users=user_nikita) & (Q(status=wekan.StatusEnum.COMPLETED) | Q(info__hours__lt=10))
):
print(f'card(card_id={card.card_id}, status={card.status}, '
f'hours={card.info.hours}, last_activity={card.last_activity})')
card(card_id=qxjWZYNc8ePMgPoXM, status=StatusEnum.COMPLETED, hours=20, last_activity=2023-02-15 10:05:27.953000)
card(card_id=bzTYbozRdzB9Wtvos, status=StatusEnum.NEW, hours=0, last_activity=2023-02-12 10:59:00.401000)
card(card_id=gwSzomsy5a3ZHo4wE, status=StatusEnum.COMPLETED, hours=10, last_activity=2023-02-15 10:05:56.718000)
card(card_id=jwEezjR4tDN3opKGL, status=StatusEnum.COMPLETED, hours=20, last_activity=2023-01-19 15:30:44.103000)
card(card_id=rLWW5XjB6xmDuAmSh, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:50:21.459000)
card(card_id=k5thuzX6TyjwwbbqL, status=StatusEnum.COMPLETED, hours=25, last_activity=2022-11-25 16:54:25.454000)
card(card_id=RY2v3rA2bXfkFwNvW, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:08:24.244000)
card(card_id=K2pFvw2rHXtwFioBM, status=StatusEnum.COMPLETED, hours=15, last_activity=2023-01-19 15:31:20.996000)
card(card_id=RkfftvF2AZ2dDyjWQ, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-20 12:09:21.997000)
card(card_id=uSRScYdhmabZ9ZEM8, status=StatusEnum.COMPLETED, hours=30, last_activity=2022-11-13 14:27:58.812000)
card(card_id=WtvLB2z6DSLeXdRS5, status=StatusEnum.COMPLETED, hours=9, last_activity=2022-11-13 14:25:35.786000)
card(card_id=ni7EJiR969Em5EKCf, status=StatusEnum.COMPLETED, hours=15, last_activity=2022-11-13 14:23:51.091000)
card(card_id=y8TNjJy4SADMMWSNP, status=StatusEnum.ARCHIVE, hours=5, last_activity=2022-09-17 14:16:30.957000)
card(card_id=PQw6JLAjQx4HKJrNo, status=StatusEnum.ARCHIVE, hours=5, last_activity=2022-09-17 14:16:31.885000)
card(card_id=w93xXWPQk82As98Q4, status=StatusEnum.COMPLETED, hours=5, last_activity=2023-02-15 10:08:34.423000)
С агрегации лично я не работал, потому сложно сказать что там и как, но в целом если нужна простая агрегация mongoengine должен справится, больше информации тут