datanommer.models 1.2.0__py3-none-any.whl → 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- datanommer/models/__init__.py +99 -16
- datanommer/models/alembic/versions/429e6f2cba6f_message_agent_name.py +24 -0
- datanommer/models/alembic/versions/f6918385051f_messages_headers_index.py +29 -0
- datanommer/models/testing/__init__.py +12 -4
- {datanommer_models-1.2.0.dist-info → datanommer_models-1.4.0.dist-info}/METADATA +2 -1
- datanommer_models-1.4.0.dist-info/RECORD +12 -0
- datanommer_models-1.2.0.dist-info/RECORD +0 -10
- {datanommer_models-1.2.0.dist-info → datanommer_models-1.4.0.dist-info}/LICENSE +0 -0
- {datanommer_models-1.2.0.dist-info → datanommer_models-1.4.0.dist-info}/WHEEL +0 -0
datanommer/models/__init__.py
CHANGED
@@ -32,6 +32,7 @@ from sqlalchemy import (
|
|
32
32
|
event,
|
33
33
|
ForeignKey,
|
34
34
|
func,
|
35
|
+
Index,
|
35
36
|
Integer,
|
36
37
|
not_,
|
37
38
|
or_,
|
@@ -158,6 +159,7 @@ def add(message):
|
|
158
159
|
timestamp=sent_at,
|
159
160
|
msg=message.body,
|
160
161
|
headers=headers,
|
162
|
+
agent_name=getattr(message, "agent_name", None),
|
161
163
|
users=usernames,
|
162
164
|
packages=packages,
|
163
165
|
)
|
@@ -168,7 +170,7 @@ def add(message):
|
|
168
170
|
# https://docs.sqlalchemy.org/en/14/core/custom_types.html#marshal-json-strings
|
169
171
|
|
170
172
|
|
171
|
-
class
|
173
|
+
class _JSONEncodedDict(TypeDecorator):
|
172
174
|
"""Represents an immutable structure as a json-encoded string."""
|
173
175
|
|
174
176
|
impl = UnicodeText
|
@@ -213,7 +215,15 @@ packages_assoc_table = Table(
|
|
213
215
|
|
214
216
|
class Message(DeclarativeBase):
|
215
217
|
__tablename__ = "messages"
|
216
|
-
__table_args__ = (
|
218
|
+
__table_args__ = (
|
219
|
+
UniqueConstraint("msg_id", "timestamp"),
|
220
|
+
Index(
|
221
|
+
"ix_messages_headers",
|
222
|
+
"headers",
|
223
|
+
postgresql_using="gin",
|
224
|
+
postgresql_ops={"headers": "jsonb_path_ops"},
|
225
|
+
),
|
226
|
+
)
|
217
227
|
|
218
228
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
219
229
|
msg_id = Column(Unicode, nullable=True, default=None, index=True)
|
@@ -223,11 +233,11 @@ class Message(DeclarativeBase):
|
|
223
233
|
certificate = Column(UnicodeText)
|
224
234
|
signature = Column(UnicodeText)
|
225
235
|
category = Column(Unicode, nullable=False, index=True)
|
226
|
-
|
236
|
+
agent_name = Column(Unicode, index=True)
|
227
237
|
crypto = Column(UnicodeText)
|
228
238
|
source_name = Column(Unicode, default="datanommer")
|
229
239
|
source_version = Column(Unicode, default=lambda context: __version__)
|
230
|
-
msg = Column(
|
240
|
+
msg = Column(_JSONEncodedDict, nullable=False)
|
231
241
|
headers = Column(postgresql.JSONB(none_as_null=True))
|
232
242
|
users = relationship(
|
233
243
|
"User",
|
@@ -326,7 +336,8 @@ class Message(DeclarativeBase):
|
|
326
336
|
timestamp=self.timestamp,
|
327
337
|
certificate=self.certificate,
|
328
338
|
signature=self.signature,
|
329
|
-
|
339
|
+
agent_name=self.agent_name,
|
340
|
+
username=self.agent_name, # DEPRECATED
|
330
341
|
crypto=self.crypto,
|
331
342
|
msg=self.msg,
|
332
343
|
headers=self.headers,
|
@@ -358,14 +369,21 @@ class Message(DeclarativeBase):
|
|
358
369
|
)
|
359
370
|
return self.as_dict(request)
|
360
371
|
|
372
|
+
@property
|
373
|
+
def username(self):
|
374
|
+
warn(
|
375
|
+
"The username attribute has been renamed to agent_name, and will be removed "
|
376
|
+
"in the next major version",
|
377
|
+
DeprecationWarning,
|
378
|
+
stacklevel=2,
|
379
|
+
)
|
380
|
+
return self.agent_name
|
381
|
+
|
361
382
|
@classmethod
|
362
|
-
def
|
383
|
+
def make_query(
|
363
384
|
cls,
|
364
385
|
start=None,
|
365
386
|
end=None,
|
366
|
-
page=1,
|
367
|
-
rows_per_page=100,
|
368
|
-
order="asc",
|
369
387
|
msg_id=None,
|
370
388
|
users=None,
|
371
389
|
not_users=None,
|
@@ -375,8 +393,9 @@ class Message(DeclarativeBase):
|
|
375
393
|
not_categories=None,
|
376
394
|
topics=None,
|
377
395
|
not_topics=None,
|
396
|
+
agents=None,
|
397
|
+
not_agents=None,
|
378
398
|
contains=None,
|
379
|
-
defer=False,
|
380
399
|
):
|
381
400
|
"""Flexible query interface for messages.
|
382
401
|
|
@@ -405,10 +424,6 @@ class Message(DeclarativeBase):
|
|
405
424
|
(user == 'ralph') AND
|
406
425
|
NOT (category == 'bodhi' OR category == 'wiki')
|
407
426
|
|
408
|
-
----
|
409
|
-
|
410
|
-
If the `defer` argument evaluates to True, the query won't actually
|
411
|
-
be executed, but a SQLAlchemy query object returned instead.
|
412
427
|
"""
|
413
428
|
|
414
429
|
users = users or []
|
@@ -419,6 +434,8 @@ class Message(DeclarativeBase):
|
|
419
434
|
not_cats = not_categories or []
|
420
435
|
topics = topics or []
|
421
436
|
not_topics = not_topics or []
|
437
|
+
agents = agents or []
|
438
|
+
not_agents = not_agents or []
|
422
439
|
contains = contains or []
|
423
440
|
|
424
441
|
Message = cls
|
@@ -450,6 +467,9 @@ class Message(DeclarativeBase):
|
|
450
467
|
if topics:
|
451
468
|
query = query.where(or_(*(Message.topic == topic for topic in topics)))
|
452
469
|
|
470
|
+
if agents:
|
471
|
+
query = query.where(or_(*(Message.agent_name == agent for agent in agents)))
|
472
|
+
|
453
473
|
if contains:
|
454
474
|
query = query.where(or_(*(Message.msg.like(f"%{contain}%") for contain in contains)))
|
455
475
|
|
@@ -468,23 +488,86 @@ class Message(DeclarativeBase):
|
|
468
488
|
if not_topics:
|
469
489
|
query = query.where(not_(or_(*(Message.topic == topic for topic in not_topics))))
|
470
490
|
|
491
|
+
if not_agents:
|
492
|
+
query = query.where(not_(or_(*(Message.agent_name == agent for agent in not_agents))))
|
493
|
+
|
494
|
+
return query
|
495
|
+
|
496
|
+
@classmethod
|
497
|
+
def grep(
|
498
|
+
cls,
|
499
|
+
*,
|
500
|
+
page=1,
|
501
|
+
rows_per_page=100,
|
502
|
+
order="asc",
|
503
|
+
defer=False,
|
504
|
+
**kwargs,
|
505
|
+
):
|
506
|
+
"""Flexible query interface for messages.
|
507
|
+
|
508
|
+
Arguments are filters. start and end should be :mod:`datetime` objs.
|
509
|
+
|
510
|
+
Other filters should be lists of strings. They are applied in a
|
511
|
+
conjunctive-normal-form (CNF) kind of way
|
512
|
+
|
513
|
+
for example, the following::
|
514
|
+
|
515
|
+
users = ['ralph', 'lmacken']
|
516
|
+
categories = ['bodhi', 'wiki']
|
517
|
+
|
518
|
+
should return messages where
|
519
|
+
|
520
|
+
(user=='ralph' OR user=='lmacken') AND
|
521
|
+
(category=='bodhi' OR category=='wiki')
|
522
|
+
|
523
|
+
Furthermore, you can use a negative version of each argument.
|
524
|
+
|
525
|
+
users = ['ralph']
|
526
|
+
not_categories = ['bodhi', 'wiki']
|
527
|
+
|
528
|
+
should return messages where
|
529
|
+
|
530
|
+
(user == 'ralph') AND
|
531
|
+
NOT (category == 'bodhi' OR category == 'wiki')
|
532
|
+
|
533
|
+
----
|
534
|
+
|
535
|
+
If the `defer` argument evaluates to True, the query won't actually
|
536
|
+
be executed, but a SQLAlchemy query object returned instead.
|
537
|
+
"""
|
538
|
+
query = cls.make_query(**kwargs)
|
471
539
|
# Finally, tag on our pagination arguments
|
472
|
-
|
540
|
+
Message = cls
|
541
|
+
|
542
|
+
query_total = query.with_only_columns(func.count(Message.id))
|
543
|
+
total = None
|
473
544
|
query = query.order_by(getattr(Message.timestamp, order)())
|
474
545
|
|
475
546
|
if not rows_per_page:
|
476
547
|
pages = 1
|
477
548
|
else:
|
549
|
+
total = session.scalar(query_total)
|
478
550
|
pages = int(math.ceil(total / float(rows_per_page)))
|
479
551
|
query = query.offset(rows_per_page * (page - 1)).limit(rows_per_page)
|
480
552
|
|
481
553
|
if defer:
|
482
|
-
|
554
|
+
if total is None:
|
555
|
+
total = session.scalar(query_total)
|
556
|
+
return total, pages, query
|
483
557
|
else:
|
484
558
|
# Execute!
|
485
559
|
messages = session.scalars(query).all()
|
560
|
+
if pages == 1:
|
561
|
+
total = len(messages)
|
486
562
|
return total, pages, messages
|
487
563
|
|
564
|
+
@classmethod
|
565
|
+
def get_first(cls, *, order="asc", **kwargs):
|
566
|
+
"""Get the first message matching the regular grep filters."""
|
567
|
+
query = cls.make_query(**kwargs)
|
568
|
+
query = query.order_by(getattr(Message.timestamp, order)()).limit(1)
|
569
|
+
return session.scalars(query).first()
|
570
|
+
|
488
571
|
|
489
572
|
class NamedSingleton:
|
490
573
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
@@ -0,0 +1,24 @@
|
|
1
|
+
"""Message.username → Message.agent_name
|
2
|
+
|
3
|
+
Revision ID: 429e6f2cba6f
|
4
|
+
Revises: 951c40020acc
|
5
|
+
Create Date: 2024-06-07 09:12:33.393757
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from alembic import op
|
10
|
+
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "429e6f2cba6f"
|
14
|
+
down_revision = "f6918385051f"
|
15
|
+
|
16
|
+
|
17
|
+
def upgrade():
|
18
|
+
op.alter_column("messages", "username", new_column_name="agent_name")
|
19
|
+
op.create_index(op.f("ix_messages_agent_name"), "messages", ["agent_name"], unique=False)
|
20
|
+
|
21
|
+
|
22
|
+
def downgrade():
|
23
|
+
op.drop_index(op.f("ix_messages_agent_name"), table_name="messages")
|
24
|
+
op.alter_column("messages", "agent_name", new_column_name="username")
|
@@ -0,0 +1,29 @@
|
|
1
|
+
"""Messages.headers index
|
2
|
+
|
3
|
+
Revision ID: f6918385051f
|
4
|
+
Revises: 951c40020acc
|
5
|
+
Create Date: 2024-05-07 16:05:05.344863
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from alembic import op
|
10
|
+
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "f6918385051f"
|
14
|
+
down_revision = "951c40020acc"
|
15
|
+
|
16
|
+
|
17
|
+
def upgrade():
|
18
|
+
op.create_index(
|
19
|
+
"ix_messages_headers",
|
20
|
+
"messages",
|
21
|
+
["headers"],
|
22
|
+
unique=False,
|
23
|
+
postgresql_using="gin",
|
24
|
+
postgresql_ops={"headers": "jsonb_path_ops"},
|
25
|
+
)
|
26
|
+
|
27
|
+
|
28
|
+
def downgrade():
|
29
|
+
op.drop_index("ix_messages_headers", table_name="messages", postgresql_using="gin")
|
@@ -21,8 +21,8 @@ def datanommer_db_url(postgresql_proc):
|
|
21
21
|
)
|
22
22
|
|
23
23
|
|
24
|
-
@pytest.fixture()
|
25
|
-
def
|
24
|
+
@pytest.fixture(scope="session")
|
25
|
+
def datanommer_db_engine(postgresql_proc, datanommer_db_url):
|
26
26
|
with DatabaseJanitor(
|
27
27
|
user=postgresql_proc.user,
|
28
28
|
host=postgresql_proc.host,
|
@@ -32,12 +32,20 @@ def datanommer_db(postgresql_proc, datanommer_db_url):
|
|
32
32
|
# template_dbname=postgresql_proc.template_dbname,
|
33
33
|
version=postgresql_proc.version,
|
34
34
|
):
|
35
|
-
engine = sa.create_engine(datanommer_db_url, future=True
|
35
|
+
engine = sa.create_engine(datanommer_db_url, future=True)
|
36
36
|
# Renew the global object, dm.init checks a custom attribute
|
37
37
|
dm.session = scoped_session(dm.maker)
|
38
38
|
dm.init(engine=engine, create=True)
|
39
39
|
yield engine
|
40
|
-
|
40
|
+
engine.dispose()
|
41
|
+
|
42
|
+
|
43
|
+
@pytest.fixture()
|
44
|
+
def datanommer_db(datanommer_db_url, datanommer_db_engine):
|
45
|
+
for table in reversed(dm.DeclarativeBase.metadata.sorted_tables):
|
46
|
+
dm.session.execute(table.delete())
|
47
|
+
dm.session.commit()
|
48
|
+
yield datanommer_db_engine
|
41
49
|
|
42
50
|
|
43
51
|
@pytest.fixture()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: datanommer.models
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.4.0
|
4
4
|
Summary: SQLAlchemy models for datanommer
|
5
5
|
Home-page: https://github.com/fedora-infra/datanommer
|
6
6
|
License: GPL-3.0-or-later
|
@@ -32,6 +32,7 @@ Requires-Dist: fmn-messages ; extra == "schemas"
|
|
32
32
|
Requires-Dist: kerneltest-messages (>=1.0.0,<2.0.0) ; extra == "schemas"
|
33
33
|
Requires-Dist: koji-fedoramessaging-messages (>=1.2.2,<2.0.0) ; extra == "schemas"
|
34
34
|
Requires-Dist: koschei-messages ; extra == "schemas"
|
35
|
+
Requires-Dist: maubot-fedora-messages ; extra == "schemas"
|
35
36
|
Requires-Dist: mdapi-messages ; extra == "schemas"
|
36
37
|
Requires-Dist: mediawiki-messages ; extra == "schemas"
|
37
38
|
Requires-Dist: meetbot-messages ; extra == "schemas"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
datanommer/models/__init__.py,sha256=5By2KNqRy1d7abDFMT3DvhcQZ4UVrx92XokAYevjKnk,19322
|
2
|
+
datanommer/models/alembic/env.py,sha256=WNTimgnH70CakhvuV5QCilCnOcjTy7kcx0nD7hryYx0,2793
|
3
|
+
datanommer/models/alembic/script.py.mako,sha256=D8kFI44_9vBJZrAYSkZxDTX2-S5Y-oEetEd9BKlo9S8,412
|
4
|
+
datanommer/models/alembic/versions/429e6f2cba6f_message_agent_name.py,sha256=JT_q5oweCN8soqMTul1vEspWvqx5TXOfG3ifB9DYmgs,612
|
5
|
+
datanommer/models/alembic/versions/5db25abc63be_init.py,sha256=xMD7WGCOqeVNFroCZds_aS_jta2yTrAHc_XhtmZLZRs,249
|
6
|
+
datanommer/models/alembic/versions/951c40020acc_unique.py,sha256=GwKDhppKW7y5BUV8BqILZCAiR7GsNyIvoTXUu2A1ZMI,843
|
7
|
+
datanommer/models/alembic/versions/f6918385051f_messages_headers_index.py,sha256=vj-yzMOH3MNdQDufPLAxhix74fV_2tzBbEc6JNWt9Og,575
|
8
|
+
datanommer/models/testing/__init__.py,sha256=wwAZ-s1U4M7nYNxHgJsn5ZIqLuHMzHrJwGJOwgHAFgc,1693
|
9
|
+
datanommer_models-1.4.0.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
10
|
+
datanommer_models-1.4.0.dist-info/METADATA,sha256=F6nCFWELLinPdPJVmvxwGN4a9wgB7RodZAOlPlA0KLM,2561
|
11
|
+
datanommer_models-1.4.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
12
|
+
datanommer_models-1.4.0.dist-info/RECORD,,
|
@@ -1,10 +0,0 @@
|
|
1
|
-
datanommer/models/__init__.py,sha256=ImCJYqPGAPBjx5OoVu6Lbo0mC6Zu9buGfrdgbectsTc,16874
|
2
|
-
datanommer/models/alembic/env.py,sha256=WNTimgnH70CakhvuV5QCilCnOcjTy7kcx0nD7hryYx0,2793
|
3
|
-
datanommer/models/alembic/script.py.mako,sha256=D8kFI44_9vBJZrAYSkZxDTX2-S5Y-oEetEd9BKlo9S8,412
|
4
|
-
datanommer/models/alembic/versions/5db25abc63be_init.py,sha256=xMD7WGCOqeVNFroCZds_aS_jta2yTrAHc_XhtmZLZRs,249
|
5
|
-
datanommer/models/alembic/versions/951c40020acc_unique.py,sha256=GwKDhppKW7y5BUV8BqILZCAiR7GsNyIvoTXUu2A1ZMI,843
|
6
|
-
datanommer/models/testing/__init__.py,sha256=T_uy2jBp5TG507WAVTx4A96_SsFgy4UrY6WKOuG_l1Q,1453
|
7
|
-
datanommer_models-1.2.0.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
8
|
-
datanommer_models-1.2.0.dist-info/METADATA,sha256=KxGFi0ZMyjdK7oslLs8ImIlD9oHogCWF4MXOj2Yyg0k,2502
|
9
|
-
datanommer_models-1.2.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
10
|
-
datanommer_models-1.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|