diff --git a/mobilizon_bots/event/event.py b/mobilizon_bots/event/event.py index 8f522fc..81460cd 100644 --- a/mobilizon_bots/event/event.py +++ b/mobilizon_bots/event/event.py @@ -1,12 +1,22 @@ from dataclasses import dataclass, asdict -from enum import Enum +from enum import IntEnum from typing import Optional import arrow from jinja2 import Template +import tortoise.timezone + +from mobilizon_bots.models.event import Event -class PublicationStatus(Enum): +class PublicationStatus(IntEnum): + WAITING = 1 + FAILED = 2 + PARTIAL = 3 + COMPLETED = 4 + + +class NotificationStatus(IntEnum): WAITING = 1 FAILED = 2 PARTIAL = 3 @@ -29,7 +39,7 @@ class MobilizonEvent: publication_status: PublicationStatus = PublicationStatus.WAITING def __post_init__(self): - + assert self.begin_datetime.tzinfo == self.end_datetime.tzinfo assert self.begin_datetime < self.end_datetime if self.publication_time: assert self.publication_status in [ @@ -42,3 +52,36 @@ class MobilizonEvent: def format(self, pattern: Template) -> str: return self._fill_template(pattern) + + def to_model(self) -> Event: + return Event( + name=self.name, + description=self.description, + mobilizon_id=self.mobilizon_id, + mobilizon_link=self.mobilizon_link, + thumbnail_link=self.thumbnail_link, + location=self.location, + begin_datetime=self.begin_datetime.astimezone(self.begin_datetime.tzinfo), + end_datetime=self.end_datetime.astimezone(self.end_datetime.tzinfo), + ) + + @staticmethod + def from_model(event: Event, tz: str = "UTC"): + # await Event.filter(id=event.id).values("id", "name", tournament="tournament__name") + return MobilizonEvent( + name=event.name, + description=event.description, + begin_datetime=arrow.get( + tortoise.timezone.localtime(value=event.begin_datetime, timezone=tz) + ), + end_datetime=arrow.get( + tortoise.timezone.localtime(value=event.end_datetime, timezone=tz) + ), + mobilizon_link=event.mobilizon_link, + mobilizon_id=event.mobilizon_id, + thumbnail_link=event.thumbnail_link, + location=event.location, + # TODO: Discuss publications + # publication_time=tortoise.timezone.localtime(value=event.publications, timezone=tz), + # publication_status=PublicationStatus.WAITING + ) diff --git a/mobilizon_bots/models/event.py b/mobilizon_bots/models/event.py index 038d9c1..066b0f8 100644 --- a/mobilizon_bots/models/event.py +++ b/mobilizon_bots/models/event.py @@ -3,16 +3,24 @@ from tortoise.models import Model class Event(Model): - id = fields.UUID(pk=True) + id = fields.UUIDField(pk=True) name = fields.TextField() + description = fields.TextField() - # tournament = fields.ForeignKeyField('models.Tournament', related_name='events') - # participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team') - # modified = fields.DatetimeField(auto_now=True) - # prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True) + mobilizon_id = fields.TextField() + mobilizon_link = fields.TextField() + thumbnail_link = fields.TextField(null=True) + + location = fields.TextField(null=True) + + begin_datetime = fields.DatetimeField() + end_datetime = fields.DatetimeField() def __str__(self): return self.name + def __repr__(self): + return f"{self.id} - {self.name}" + class Meta: table = "event" diff --git a/mobilizon_bots/models/notification.py b/mobilizon_bots/models/notification.py new file mode 100644 index 0000000..82f72df --- /dev/null +++ b/mobilizon_bots/models/notification.py @@ -0,0 +1,25 @@ +from tortoise import fields +from tortoise.models import Model + +from mobilizon_bots.event.event import NotificationStatus + + +class Notification(Model): + id = fields.UUIDField(pk=True) + status = fields.IntEnumField(NotificationStatus) + + message = fields.TextField() + + target = fields.ForeignKeyField( + "models.Publisher", related_name="notifications", null=True + ) + + publication = fields.ForeignKeyField( + "models.Publication", related_name="notifications", null=True + ) + + def __str__(self): + return f"[{self.status}] {self.message}" + + class Meta: + table = "notification" diff --git a/mobilizon_bots/models/publication.py b/mobilizon_bots/models/publication.py new file mode 100644 index 0000000..82387a6 --- /dev/null +++ b/mobilizon_bots/models/publication.py @@ -0,0 +1,20 @@ +from tortoise import fields +from tortoise.models import Model + +from mobilizon_bots.event.event import PublicationStatus + + +class Publication(Model): + id = fields.UUIDField(pk=True) + status = fields.IntEnumField(PublicationStatus) + + timestamp = fields.DatetimeField() + + event = fields.ForeignKeyField("models.Event", related_name="publications") + publisher = fields.ForeignKeyField("models.Publisher", related_name="publications") + + def __str__(self): + return f"{self.id}" + + class Meta: + table = "publication" diff --git a/mobilizon_bots/models/publisher.py b/mobilizon_bots/models/publisher.py new file mode 100644 index 0000000..67ebfde --- /dev/null +++ b/mobilizon_bots/models/publisher.py @@ -0,0 +1,14 @@ +from tortoise import fields +from tortoise.models import Model + + +class Publisher(Model): + id = fields.UUIDField(pk=True) + type = fields.CharField(max_length=255) + account_ref = fields.CharField(max_length=512) + + def __str__(self): + return f"{self.id}" + + class Meta: + table = "publisher" diff --git a/mobilizon_bots/storage/db.py b/mobilizon_bots/storage/db.py index 740226f..e30e988 100644 --- a/mobilizon_bots/storage/db.py +++ b/mobilizon_bots/storage/db.py @@ -1,5 +1,3 @@ -import asyncio -import atexit import logging from pathlib import Path @@ -10,25 +8,29 @@ logger = logging.getLogger(__name__) class MobilizonBotsDB: - def __init__(self, path: Path = None): + def __init__(self, path: Path): self.path = path + # TODO: Check if DB is openable/"queriable" + self.is_init = self.path.exists() and (not self.path.is_dir()) + if not self.is_init: + self.path.parent.mkdir(parents=True, exist_ok=True) async def setup(self): + # TODO: Caricare i publishers. await Tortoise.init( db_url=f"sqlite:///{self.path}", - modules={"models": ["mobilizon_bots.event.model"]}, + modules={ + "models": [ + "mobilizon_bots.models.event", + "mobilizon_bots.models.notification", + "mobilizon_bots.models.publication", + "mobilizon_bots.models.publisher", + ] + }, + # always store UTC time in database + use_tz=True, ) - if not self.is_init(): - # Generate the schema + if not self.is_init: await Tortoise.generate_schemas() - - def is_init(self) -> bool: - # TODO: Check if DB is openable/"queriable" - return self.path.exists() and (not self.path.is_dir()) - - -@atexit.register -def gracefully_tear_down(): - logger.info("Shutting down DB") - loop = asyncio.get_event_loop() - loop.run_until_complete(Tortoise.close_connections()) + self.is_init = True + logger.info(f"Succesfully initialized database at {self.path}") diff --git a/mobilizon_bots/storage/query.py b/mobilizon_bots/storage/query.py new file mode 100644 index 0000000..be6d301 --- /dev/null +++ b/mobilizon_bots/storage/query.py @@ -0,0 +1,18 @@ +from typing import Iterable + +from mobilizon_bots.event.event import MobilizonEvent, PublicationStatus +from mobilizon_bots.models.event import Event + + +async def get_published_events() -> Iterable[MobilizonEvent]: + return map( + MobilizonEvent.from_model, + await Event.filter( + publications__status__in=[ + PublicationStatus.COMPLETED, + PublicationStatus.PARTIAL, + ] + ) + .order_by("begin_datetime") + .distinct(), + ) diff --git a/poetry.lock b/poetry.lock index 7866f93..95fecc2 100644 --- a/poetry.lock +++ b/poetry.lock @@ -20,6 +20,14 @@ python-versions = ">=3.6" [package.dependencies] python-dateutil = ">=2.7.0" +[[package]] +name = "asynctest" +version = "0.13.0" +description = "Enhance the standard unittest package with features for testing asyncio libraries" +category = "dev" +optional = false +python-versions = ">=3.5" + [[package]] name = "atomicwrites" version = "1.4.0" @@ -30,21 +38,21 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "20.3.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"] -docs = ["furo", "sphinx", "zope.interface"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] [[package]] name = "certifi" -version = "2020.12.5" +version = "2021.5.30" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -115,15 +123,15 @@ i18n = ["Babel (>=0.8)"] [[package]] name = "markupsafe" -version = "1.1.1" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" +python-versions = ">=3.6" [[package]] name = "more-itertools" -version = "8.7.0" +version = "8.8.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false @@ -169,7 +177,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pypika-tortoise" -version = "0.1.0" +version = "0.1.1" description = "Forked from pypika and streamline just for tortoise-orm" category = "main" optional = false @@ -197,6 +205,20 @@ wcwidth = "*" checkqa-mypy = ["mypy (==v0.761)"] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +[[package]] +name = "pytest-asyncio" +version = "0.10.0" +description = "Pytest support for asyncio." +category = "dev" +optional = false +python-versions = ">= 3.5" + +[package.dependencies] +pytest = ">=3.0.6" + +[package.extras] +testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=3.64)"] + [[package]] name = "python-dateutil" version = "2.8.1" @@ -210,7 +232,7 @@ six = ">=1.5" [[package]] name = "pytz" -version = "2020.5" +version = "2021.1" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -260,7 +282,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tortoise-orm" -version = "0.17.2" +version = "0.17.3" description = "Easy async ORM for python, built with relations in mind" category = "main" optional = false @@ -269,8 +291,8 @@ python-versions = ">=3.7,<4.0" [package.dependencies] aiosqlite = ">=0.16.0,<0.17.0" iso8601 = ">=0.1.13,<0.2.0" -pypika-tortoise = ">=0.1.0,<0.2.0" -pytz = ">=2020.4,<2021.0" +pypika-tortoise = ">=0.1.1,<0.2.0" +pytz = "*" [package.extras] docs = ["pygments", "cloud-sptheme", "docutils", "sphinx"] @@ -289,16 +311,16 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.26.4" +version = "1.26.5" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] +brotli = ["brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] -brotli = ["brotlipy (>=0.6.0)"] [[package]] name = "wcwidth" @@ -311,7 +333,7 @@ python-versions = "*" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "be81df312539b9c6d1020d05e5001c8819f5ca47435a84e398b35cd39020e7bf" +content-hash = "e7498886449b92f3665ecb67bbc223063789c03ddcc7a35b5dee9dd9132b56b2" [metadata.files] aiosqlite = [ @@ -322,17 +344,21 @@ arrow = [ {file = "arrow-1.1.0-py3-none-any.whl", hash = "sha256:8cbe6a629b1c54ae11b52d6d9e70890089241958f63bc59467e277e34b7a5378"}, {file = "arrow-1.1.0.tar.gz", hash = "sha256:b8fe13abf3517abab315e09350c903902d1447bd311afbc17547ba1cb3ff5bd8"}, ] +asynctest = [ + {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, + {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, +] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"}, - {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] certifi = [ - {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"}, - {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"}, + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] chardet = [ {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, @@ -359,38 +385,44 @@ jinja2 = [ {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] markupsafe = [ - {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"}, - {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"}, - {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"}, - {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"}, - {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"}, - {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"}, - {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"}, - {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, + {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, + {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, + {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, + {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, + {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, ] more-itertools = [ - {file = "more-itertools-8.7.0.tar.gz", hash = "sha256:c5d6da9ca3ff65220c3bfd2a8db06d698f05d4d2b9be57e1deb2be5a45019713"}, - {file = "more_itertools-8.7.0-py3-none-any.whl", hash = "sha256:5652a9ac72209ed7df8d9c15daf4e1aa0e3d2ccd3c87f8265a0673cd9cbc9ced"}, + {file = "more-itertools-8.8.0.tar.gz", hash = "sha256:83f0308e05477c68f56ea3a888172c78ed5d5b3c282addb67508e7ba6c8f813a"}, + {file = "more_itertools-8.8.0-py3-none-any.whl", hash = "sha256:2cf89ec599962f2ddc4d568a05defc40e0a587fbc10d5989713638864c36be4d"}, ] packaging = [ {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, @@ -409,20 +441,24 @@ pyparsing = [ {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pypika-tortoise = [ - {file = "pypika-tortoise-0.1.0.tar.gz", hash = "sha256:7176e98ff0cf7c311d4ba58f28f1755956265dee2f9781e65e1304a67a3e5aa5"}, - {file = "pypika_tortoise-0.1.0-py3-none-any.whl", hash = "sha256:ec83b0b2964be01ef563f5f019b0332a18177604e841c47ad39d798798c6dfe9"}, + {file = "pypika-tortoise-0.1.1.tar.gz", hash = "sha256:6831d0a56e5e0ecefac3307dd9bdb3e5073fdac5d617401601d3a6713e059f3c"}, + {file = "pypika_tortoise-0.1.1-py3-none-any.whl", hash = "sha256:860020094e01058ea80602c90d4a843d0a42cffefcf4f3cb1a7f2c18b880c638"}, ] pytest = [ {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"}, {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"}, ] +pytest-asyncio = [ + {file = "pytest-asyncio-0.10.0.tar.gz", hash = "sha256:9fac5100fd716cbecf6ef89233e8590a4ad61d729d1732e0a96b84182df1daaf"}, + {file = "pytest_asyncio-0.10.0-py3-none-any.whl", hash = "sha256:d734718e25cfc32d2bf78d346e99d33724deeba774cc4afdf491530c6184b63b"}, +] python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"}, ] pytz = [ - {file = "pytz-2020.5-py2.py3-none-any.whl", hash = "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4"}, - {file = "pytz-2020.5.tar.gz", hash = "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5"}, + {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, + {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, ] requests = [ {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, @@ -437,8 +473,8 @@ six = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] tortoise-orm = [ - {file = "tortoise-orm-0.17.2.tar.gz", hash = "sha256:1a742b2f15a31d47a8dea7706b478cc9a7ce9af268b61d77d0fa22cfbaea271a"}, - {file = "tortoise_orm-0.17.2-py3-none-any.whl", hash = "sha256:b0c02be3800398053058377ddca91fa051eb98eebb704d2db2a3ab1c6a58e347"}, + {file = "tortoise-orm-0.17.3.tar.gz", hash = "sha256:6e5e56694b64118faaada2670343c909d6c9f84c7235f3372b8a398b2e8d6628"}, + {file = "tortoise_orm-0.17.3-py3-none-any.whl", hash = "sha256:99b448a870a81b6edb3ef9d2f0e22b2c83afa2b6348178840d3ccdbf03e206d3"}, ] typing-extensions = [ {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, @@ -446,8 +482,8 @@ typing-extensions = [ {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] urllib3 = [ - {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"}, - {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"}, + {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, + {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"}, ] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, diff --git a/pyproject.toml b/pyproject.toml index 4e298b7..2969b01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,8 +14,10 @@ requests = "^2.25.1" arrow = "^1.1.0" [tool.poetry.dev-dependencies] +asynctest = "^0.13" pytest = "^5.3" -responses = "^0.13.3" +responses = "^0.13" +pytest-asyncio = "^0.10" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tests/conftest.py b/tests/conftest.py index 4b6e989..9f4471e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,10 +1,24 @@ import arrow import pkg_resources +import os +from datetime import datetime, timedelta, timezone + import pytest + from dynaconf import settings +from tortoise.contrib.test import finalizer, initializer from mobilizon_bots.config.config import build_and_validate_settings -from mobilizon_bots.event.event import MobilizonEvent, PublicationStatus + +from mobilizon_bots.event.event import ( + MobilizonEvent, + PublicationStatus, + NotificationStatus, +) +from mobilizon_bots.models.event import Event +from mobilizon_bots.models.notification import Notification +from mobilizon_bots.models.publication import Publication +from mobilizon_bots.models.publisher import Publisher def generate_publication_status(published): @@ -21,6 +35,10 @@ def set_test_settings(): build_and_validate_settings([config_file]) +def generate_notification_status(published): + return NotificationStatus.COMPLETED if published else NotificationStatus.WAITING + + @pytest.fixture def event_generator(): def _event_generator( @@ -48,13 +66,109 @@ def event_generator(): @pytest.fixture() def event() -> MobilizonEvent: + begin_date = arrow.get( + datetime( + year=2021, + month=1, + day=1, + hour=11, + minute=30, + tzinfo=timezone(timedelta(hours=1)), + ) + ) return MobilizonEvent( name="test event", description="description of the event", - begin_datetime=arrow.Arrow(year=2021, month=1, day=1, hour=11, minute=30), - end_datetime=arrow.Arrow(year=2021, month=1, day=1, hour=12, minute=30), + begin_datetime=begin_date, + end_datetime=begin_date.shift(hours=1), mobilizon_link="http://some_link.com/123", mobilizon_id="12345", thumbnail_link="http://some_link.com/123.jpg", location="location", ) + + +@pytest.fixture(scope="function", autouse=True) +def initialize_db_tests(request) -> None: + db_url = os.environ.get("TORTOISE_TEST_DB", "sqlite://:memory:") + initializer( + [ + "mobilizon_bots.models.event", + "mobilizon_bots.models.notification", + "mobilizon_bots.models.publication", + "mobilizon_bots.models.publisher", + ], + db_url=db_url, + app_label="models", + ) + request.addfinalizer(finalizer) + + +@pytest.fixture() +def event_model_generator(): + def _event_model_generator( + idx=1, + begin_date=datetime( + year=2021, + month=1, + day=1, + hour=11, + minute=30, + tzinfo=timezone(timedelta(hours=0)), + ), + ): + return Event( + name=f"event_{idx}", + description=f"desc_{idx}", + mobilizon_id=f"mobid_{idx}", + mobilizon_link=f"moblink_{idx}", + thumbnail_link=f"thumblink_{idx}", + location=f"loc_{idx}", + begin_datetime=begin_date, + end_datetime=begin_date + timedelta(hours=2), + ) + + return _event_model_generator + + +@pytest.fixture() +def publisher_model_generator(): + def _publisher_model_generator( + idx=1, + ): + return Publisher(type=f"publisher_{idx}", account_ref=f"account_ref_{idx}") + + return _publisher_model_generator + + +@pytest.fixture() +def publication_model_generator(): + def _publication_model_generator( + status=PublicationStatus.WAITING, + publication_time=datetime(year=2021, month=1, day=1, hour=11, minute=30), + event_id=None, + publisher_id=None, + ): + return Publication( + status=status, + timestamp=publication_time, + event_id=event_id, + publisher_id=publisher_id, + ) + + return _publication_model_generator + + +@pytest.fixture() +def notification_model_generator(): + def _notification_model_generator( + idx=1, published=False, publication_id=None, target_id=None + ): + return Notification( + status=generate_notification_status(published), + message=f"message_{idx}", + publication_id=publication_id, + target_id=target_id, + ) + + return _notification_model_generator diff --git a/tests/models/__init__.py b/tests/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/models/test_event.py b/tests/models/test_event.py new file mode 100644 index 0000000..f19802f --- /dev/null +++ b/tests/models/test_event.py @@ -0,0 +1,123 @@ +from datetime import datetime, timedelta, timezone + +import arrow +import pytest +import tortoise.timezone + +from mobilizon_bots.event.event import MobilizonEvent +from mobilizon_bots.models.event import Event + + +@pytest.mark.asyncio +async def test_event_create(event_model_generator): + event_model = event_model_generator() + await event_model.save() + event_db = await Event.filter(name="event_1").first() + assert event_db.begin_datetime == datetime( + year=2021, + month=1, + day=1, + hour=11, + minute=30, + tzinfo=timezone(timedelta(hours=0)), + ) + + +@pytest.mark.asyncio +async def test_event_timezone_storage(event_model_generator): + cet = timezone(timedelta(hours=1)) + event_cet = event_model_generator( + begin_date=datetime(year=2021, month=6, day=6, hour=5, minute=0, tzinfo=cet), + ) + await event_cet.save() + cest = timezone(timedelta(hours=2)) + event_cest = event_model_generator( + idx=2, + begin_date=datetime(year=2021, month=6, day=6, hour=6, minute=0, tzinfo=cest), + ) + await event_cest.save() + events = await Event.all() + assert len(events) == 2 + assert events[0].begin_datetime == events[1].begin_datetime + + +@pytest.mark.asyncio +async def test_event_sort_by_date(event_model_generator): + today = datetime( + 2021, + 5, + 10, + hour=0, + minute=0, + second=0, + microsecond=0, + tzinfo=timezone(timedelta(hours=1)), + ) + today_utc = datetime( + 2021, + 5, + 9, + hour=23, + minute=0, + second=0, + microsecond=0, + tzinfo=timezone(timedelta(hours=0)), + ) + event_yesterday = event_model_generator(idx=1, begin_date=today - timedelta(days=1)) + event_today = event_model_generator(idx=2, begin_date=today) + event_tomorrow = event_model_generator(idx=3, begin_date=today + timedelta(days=1)) + + await event_yesterday.save() + await event_today.save() + await event_tomorrow.save() + + events = await Event.all().order_by("begin_datetime") + + assert len(events) == 3 + + assert ( + events[0].begin_datetime < events[1].begin_datetime < events[2].begin_datetime + ) + + assert events[0].begin_datetime == today_utc - timedelta(days=1) + assert events[1].begin_datetime == today_utc + assert events[2].begin_datetime == today_utc + timedelta(days=1) + + +@pytest.mark.asyncio +async def test_mobilizon_event_to_model(event): + event_model = event.to_model() + await event_model.save() + + event_db = await Event.all().first() + begin_date_utc = arrow.Arrow(year=2021, month=1, day=1, hour=10, minute=30) + begin_date_utc = begin_date_utc.astimezone(tortoise.timezone.get_default_timezone()) + + assert event_db.name == "test event" + assert event_db.description == "description of the event" + assert event_db.begin_datetime == begin_date_utc + assert event_db.end_datetime == begin_date_utc + timedelta(hours=1) + assert event_db.mobilizon_link == "http://some_link.com/123" + assert event_db.mobilizon_id == "12345" + assert event_db.thumbnail_link == "http://some_link.com/123.jpg" + assert event_db.location == "location" + + +@pytest.mark.asyncio +async def test_mobilizon_event_from_model(event_model_generator): + event_model = event_model_generator() + await event_model.save() + + event_db = await Event.all().first() + event = MobilizonEvent.from_model(event=event_db, tz="CET") + + begin_date_utc = arrow.Arrow(year=2021, month=1, day=1, hour=11, minute=30) + + assert event.name == "event_1" + assert event.description == "desc_1" + assert event.begin_datetime == begin_date_utc + assert event.end_datetime == begin_date_utc.shift(hours=2) + assert event.mobilizon_link == "moblink_1" + assert event.mobilizon_id == "mobid_1" + assert event.thumbnail_link == "thumblink_1" + assert event.location == "loc_1" diff --git a/tests/models/test_notification.py b/tests/models/test_notification.py new file mode 100644 index 0000000..3e6ff61 --- /dev/null +++ b/tests/models/test_notification.py @@ -0,0 +1,13 @@ +import pytest + +from mobilizon_bots.event.event import NotificationStatus +from mobilizon_bots.models.notification import Notification + + +@pytest.mark.asyncio +async def test_notification_create(notification_model_generator): + notification_model = notification_model_generator() + await notification_model.save() + notification_db = await Notification.all().first() + assert notification_db.status == NotificationStatus.WAITING + assert notification_db.message == "message_1" diff --git a/tests/models/test_publication.py b/tests/models/test_publication.py new file mode 100644 index 0000000..303c581 --- /dev/null +++ b/tests/models/test_publication.py @@ -0,0 +1,31 @@ +import pytest + +from datetime import datetime +from tortoise import timezone + +from mobilizon_bots.event.event import PublicationStatus +from mobilizon_bots.models.publication import Publication + + +@pytest.mark.asyncio +async def test_publication_create( + publication_model_generator, event_model_generator, publisher_model_generator +): + event = event_model_generator() + await event.save() + publisher = publisher_model_generator() + await publisher.save() + publication_model = await publication_model_generator( + event_id=event.id, publisher_id=publisher.id + ) + await publication_model.save() + publication_db = await Publication.all().first() + assert publication_db.status == PublicationStatus.WAITING + assert publication_db.timestamp == datetime( + year=2021, + month=1, + day=1, + hour=11, + minute=30, + tzinfo=timezone.get_default_timezone(), + ) diff --git a/tests/models/test_publisher.py b/tests/models/test_publisher.py new file mode 100644 index 0000000..d76d5d0 --- /dev/null +++ b/tests/models/test_publisher.py @@ -0,0 +1,11 @@ +import pytest + +from mobilizon_bots.models.publisher import Publisher + + +@pytest.mark.asyncio +async def test_publisher_create(publisher_model_generator): + publisher_model = publisher_model_generator() + await publisher_model.save() + publisher_db = await Publisher.filter(type="publisher_1").first() + assert publisher_db.account_ref == "account_ref_1" diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/storage/test_query.py b/tests/storage/test_query.py new file mode 100644 index 0000000..f713c42 --- /dev/null +++ b/tests/storage/test_query.py @@ -0,0 +1,59 @@ +from datetime import datetime, timedelta, timezone + +import arrow +import pytest + +from mobilizon_bots.event.event import PublicationStatus +from mobilizon_bots.storage.query import get_published_events + + +@pytest.mark.asyncio +async def test_get_unpublished_events( + publisher_model_generator, publication_model_generator, event_model_generator +): + today = datetime( + year=2021, month=6, day=6, hour=5, minute=0, tzinfo=timezone(timedelta(hours=2)) + ) + publisher_1 = publisher_model_generator() + publisher_2 = publisher_model_generator(idx=2) + await publisher_1.save() + await publisher_2.save() + + event_1 = event_model_generator(begin_date=today) + event_2 = event_model_generator(idx=2, begin_date=today + timedelta(days=2)) + event_3 = event_model_generator(idx=3, begin_date=today + timedelta(days=-2)) + await event_1.save() + await event_2.save() + await event_3.save() + + publication_1 = publication_model_generator( + event_id=event_1.id, publisher_id=publisher_1.id + ) + publication_2 = publication_model_generator( + event_id=event_1.id, + publisher_id=publisher_2.id, + status=PublicationStatus.COMPLETED, + ) + publication_3 = publication_model_generator( + event_id=event_2.id, + publisher_id=publisher_1.id, + status=PublicationStatus.FAILED, + ) + publication_4 = publication_model_generator( + event_id=event_3.id, + publisher_id=publisher_2.id, + status=PublicationStatus.PARTIAL, + ) + await publication_1.save() + await publication_2.save() + await publication_3.save() + await publication_4.save() + + published_events = list(await get_published_events()) + assert len(published_events) == 2 + + assert published_events[0].name == event_3.name + assert published_events[1].name == event_1.name + + assert published_events[0].begin_datetime == arrow.get(today + timedelta(days=-2)) + assert published_events[1].begin_datetime == arrow.get(today)