datanommer.models 1.0.4__tar.gz → 1.2.0__tar.gz
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-1.2.0/NEWS.rst +72 -0
- datanommer_models-1.2.0/PKG-INFO +54 -0
- datanommer_models-1.2.0/coverage.xml +239 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/datanommer/models/__init__.py +35 -61
- datanommer_models-1.2.0/datanommer/models/testing/__init__.py +48 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/pyproject.toml +39 -53
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/tests/test_jsonencodeddict.py +10 -15
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/tests/test_model.py +144 -141
- datanommer_models-1.2.0/tox.ini +26 -0
- datanommer.models-1.0.4/NEWS.rst +0 -34
- datanommer.models-1.0.4/PKG-INFO +0 -43
- datanommer.models-1.0.4/coverage.xml +0 -239
- datanommer.models-1.0.4/datanommer/models/testing/__init__.py +0 -46
- datanommer.models-1.0.4/datanommer/models/testing/startup.sql +0 -1
- datanommer.models-1.0.4/setup.py +0 -54
- datanommer.models-1.0.4/tox.ini +0 -15
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/LICENSE +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/README.rst +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/alembic.ini +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/datanommer/models/alembic/env.py +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/datanommer/models/alembic/script.py.mako +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/datanommer/models/alembic/versions/5db25abc63be_init.py +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/datanommer/models/alembic/versions/951c40020acc_unique.py +0 -0
- {datanommer.models-1.0.4 → datanommer_models-1.2.0}/tests/conftest.py +0 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
=============
|
2
|
+
Release Notes
|
3
|
+
=============
|
4
|
+
|
5
|
+
.. towncrier release notes start
|
6
|
+
|
7
|
+
v1.2.0
|
8
|
+
======
|
9
|
+
|
10
|
+
Released on 2024-04-15.
|
11
|
+
This is a feature release that adds schema packages and upgrades the SQLAlchemy
|
12
|
+
API to the 2.0 style.
|
13
|
+
|
14
|
+
Features
|
15
|
+
^^^^^^^^
|
16
|
+
|
17
|
+
* Upgrade to the SQLAlchemy 2.0 API (`981e2a4
|
18
|
+
<https://github.com/fedora-infra/datanommer/commit/981e2a4>`_).
|
19
|
+
* Add a few schema packages to the dependencies.
|
20
|
+
|
21
|
+
Development Improvements
|
22
|
+
^^^^^^^^^^^^^^^^^^^^^^^^
|
23
|
+
|
24
|
+
* Use Ruff instead of flake8 and isort and bandit (:issue:`4f7ffaa`
|
25
|
+
`#4f7ffaa <https://github.com/fedora-infra/datanommer/issues/4f7ffaa>`_).
|
26
|
+
|
27
|
+
|
28
|
+
v1.1.0
|
29
|
+
======
|
30
|
+
|
31
|
+
Released on 2023-09-22.
|
32
|
+
This is a feature release that adds ``koji-fedoramessaging-messages`` as a
|
33
|
+
dependency to interpret koji messages, and updates a lot of our other
|
34
|
+
dependencies.
|
35
|
+
|
36
|
+
Dependency Changes
|
37
|
+
^^^^^^^^^^^^^^^^^^
|
38
|
+
|
39
|
+
* Drop support for python 3.7, add support for python 3.10 (`PR#890
|
40
|
+
<https://github.com/fedora-infra/datanommer/pull/890>`_).
|
41
|
+
* Add the ``koji-fedoramessaging-messages`` package (:issue:`1257`
|
42
|
+
`#1257 <https://github.com/fedora-infra/datanommer/issues/1257>`_).
|
43
|
+
|
44
|
+
|
45
|
+
v1.0.4
|
46
|
+
======
|
47
|
+
|
48
|
+
Released on 2022-05-31.
|
49
|
+
This is a minor release:
|
50
|
+
|
51
|
+
- adds fedora-messaging schema packages
|
52
|
+
- doesn't require a version of bodhi-messages in the dev deps
|
53
|
+
- adjusts pyproject for spec needs
|
54
|
+
- fixes integration of Alembic
|
55
|
+
|
56
|
+
|
57
|
+
v1.0.3
|
58
|
+
======
|
59
|
+
|
60
|
+
Released on 2022-03-18. This is a minor release:
|
61
|
+
|
62
|
+
- support fedora-messaging 3.0+
|
63
|
+
- update dependencies
|
64
|
+
|
65
|
+
|
66
|
+
v1.0.0
|
67
|
+
======
|
68
|
+
|
69
|
+
Released on 2022-01-17.
|
70
|
+
|
71
|
+
This is a major release that uses TimescaleDB to store the data.
|
72
|
+
The list of changes is too big to list here.
|
@@ -0,0 +1,54 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: datanommer.models
|
3
|
+
Version: 1.2.0
|
4
|
+
Summary: SQLAlchemy models for datanommer
|
5
|
+
Home-page: https://github.com/fedora-infra/datanommer
|
6
|
+
License: GPL-3.0-or-later
|
7
|
+
Author: Fedora Infrastructure
|
8
|
+
Author-email: admin@fedoraproject.org
|
9
|
+
Requires-Python: >=3.10,<4.0
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
|
+
Provides-Extra: schemas
|
16
|
+
Requires-Dist: SQLAlchemy (>=1.3.24,<3.0.0)
|
17
|
+
Requires-Dist: alembic (>=1.6.5,<2.0.0)
|
18
|
+
Requires-Dist: anitya-schema ; extra == "schemas"
|
19
|
+
Requires-Dist: bodhi-messages ; extra == "schemas"
|
20
|
+
Requires-Dist: bugzilla2fedmsg-schema ; extra == "schemas"
|
21
|
+
Requires-Dist: ci-messages ; extra == "schemas"
|
22
|
+
Requires-Dist: copr-messaging ; extra == "schemas"
|
23
|
+
Requires-Dist: discourse2fedmsg-messages ; extra == "schemas"
|
24
|
+
Requires-Dist: fedocal-messages ; extra == "schemas"
|
25
|
+
Requires-Dist: fedora-elections-messages ; extra == "schemas"
|
26
|
+
Requires-Dist: fedora-messaging (>=2.1.0)
|
27
|
+
Requires-Dist: fedora-messaging-git-hook-messages ; extra == "schemas"
|
28
|
+
Requires-Dist: fedora-messaging-the-new-hotness-schema ; extra == "schemas"
|
29
|
+
Requires-Dist: fedora-planet-messages ; extra == "schemas"
|
30
|
+
Requires-Dist: fedorainfra-ansible-messages ; extra == "schemas"
|
31
|
+
Requires-Dist: fmn-messages ; extra == "schemas"
|
32
|
+
Requires-Dist: kerneltest-messages (>=1.0.0,<2.0.0) ; extra == "schemas"
|
33
|
+
Requires-Dist: koji-fedoramessaging-messages (>=1.2.2,<2.0.0) ; extra == "schemas"
|
34
|
+
Requires-Dist: koschei-messages ; extra == "schemas"
|
35
|
+
Requires-Dist: mdapi-messages ; extra == "schemas"
|
36
|
+
Requires-Dist: mediawiki-messages ; extra == "schemas"
|
37
|
+
Requires-Dist: meetbot-messages ; extra == "schemas"
|
38
|
+
Requires-Dist: noggin-messages ; extra == "schemas"
|
39
|
+
Requires-Dist: nuancier-messages ; extra == "schemas"
|
40
|
+
Requires-Dist: pagure-messages ; extra == "schemas"
|
41
|
+
Requires-Dist: psycopg2 (>=2.9.1,<3.0.0)
|
42
|
+
Requires-Dist: tahrir-messages ; extra == "schemas"
|
43
|
+
Project-URL: Repository, https://github.com/fedora-infra/datanommer
|
44
|
+
Description-Content-Type: text/x-rst
|
45
|
+
|
46
|
+
datanommer.models
|
47
|
+
=================
|
48
|
+
|
49
|
+
This package contains the SQLAlchemy data model for datanommer.
|
50
|
+
|
51
|
+
Datanommer is a storage consumer for the Fedora Infrastructure Message Bus
|
52
|
+
(fedmsg). It is comprised of a `fedmsg <http://fedmsg.com>`_ consumer that
|
53
|
+
stuffs every message into a sqlalchemy database.
|
54
|
+
|
@@ -0,0 +1,239 @@
|
|
1
|
+
<?xml version="1.0" ?>
|
2
|
+
<coverage version="7.4.4" timestamp="1712820460896" lines-valid="220" lines-covered="217" line-rate="0.9864" branches-valid="100" branches-covered="100" branch-rate="1" complexity="0">
|
3
|
+
<!-- Generated by coverage.py: https://coverage.readthedocs.io/en/7.4.4 -->
|
4
|
+
<!-- Based on https://raw.githubusercontent.com/cobertura/web/master/htdocs/xml/coverage-04.dtd -->
|
5
|
+
<sources>
|
6
|
+
<source>/home/abompard/Fedora/apps/datanommer/datanommer.models/datanommer</source>
|
7
|
+
</sources>
|
8
|
+
<packages>
|
9
|
+
<package name="models" line-rate="0.9864" branch-rate="1" complexity="0">
|
10
|
+
<classes>
|
11
|
+
<class name="__init__.py" filename="models/__init__.py" complexity="0" line-rate="0.9864" branch-rate="1">
|
12
|
+
<methods/>
|
13
|
+
<lines>
|
14
|
+
<line number="16" hits="1"/>
|
15
|
+
<line number="17" hits="1"/>
|
16
|
+
<line number="18" hits="1"/>
|
17
|
+
<line number="19" hits="1"/>
|
18
|
+
<line number="20" hits="1"/>
|
19
|
+
<line number="21" hits="1"/>
|
20
|
+
<line number="22" hits="1"/>
|
21
|
+
<line number="23" hits="1"/>
|
22
|
+
<line number="25" hits="1"/>
|
23
|
+
<line number="47" hits="1"/>
|
24
|
+
<line number="48" hits="1"/>
|
25
|
+
<line number="49" hits="1"/>
|
26
|
+
<line number="56" hits="1"/>
|
27
|
+
<line number="59" hits="1"/>
|
28
|
+
<line number="60" hits="1"/>
|
29
|
+
<line number="67" hits="1"/>
|
30
|
+
<line number="70" hits="1"/>
|
31
|
+
<line number="72" hits="1"/>
|
32
|
+
<line number="73" hits="1"/>
|
33
|
+
<line number="75" hits="1"/>
|
34
|
+
<line number="76" hits="1"/>
|
35
|
+
<line number="79" hits="1"/>
|
36
|
+
<line number="82" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
37
|
+
<line number="83" hits="1"/>
|
38
|
+
<line number="85" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
39
|
+
<line number="86" hits="1"/>
|
40
|
+
<line number="88" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
41
|
+
<line number="89" hits="1"/>
|
42
|
+
<line number="94" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
43
|
+
<line number="95" hits="1"/>
|
44
|
+
<line number="96" hits="1"/>
|
45
|
+
<line number="97" hits="1"/>
|
46
|
+
<line number="99" hits="1"/>
|
47
|
+
<line number="100" hits="1"/>
|
48
|
+
<line number="102" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
49
|
+
<line number="103" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
50
|
+
<line number="104" hits="1"/>
|
51
|
+
<line number="105" hits="1"/>
|
52
|
+
<line number="116" hits="1"/>
|
53
|
+
<line number="120" hits="1"/>
|
54
|
+
<line number="121" hits="1"/>
|
55
|
+
<line number="123" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
56
|
+
<line number="126" hits="1"/>
|
57
|
+
<line number="127" hits="1"/>
|
58
|
+
<line number="128" hits="1"/>
|
59
|
+
<line number="129" hits="1"/>
|
60
|
+
<line number="130" hits="1"/>
|
61
|
+
<line number="132" hits="1"/>
|
62
|
+
<line number="135" hits="1"/>
|
63
|
+
<line number="136" hits="1"/>
|
64
|
+
<line number="137" hits="0"/>
|
65
|
+
<line number="138" hits="0"/>
|
66
|
+
<line number="143" hits="0"/>
|
67
|
+
<line number="144" hits="1"/>
|
68
|
+
<line number="145" hits="1"/>
|
69
|
+
<line number="146" hits="1"/>
|
70
|
+
<line number="147" hits="1"/>
|
71
|
+
<line number="152" hits="1"/>
|
72
|
+
<line number="154" hits="1"/>
|
73
|
+
<line number="165" hits="1"/>
|
74
|
+
<line number="171" hits="1"/>
|
75
|
+
<line number="174" hits="1"/>
|
76
|
+
<line number="176" hits="1"/>
|
77
|
+
<line number="178" hits="1"/>
|
78
|
+
<line number="179" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
79
|
+
<line number="180" hits="1"/>
|
80
|
+
<line number="182" hits="1"/>
|
81
|
+
<line number="184" hits="1"/>
|
82
|
+
<line number="185" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
83
|
+
<line number="186" hits="1"/>
|
84
|
+
<line number="187" hits="1"/>
|
85
|
+
<line number="189" hits="1"/>
|
86
|
+
<line number="191" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
87
|
+
<line number="192" hits="1"/>
|
88
|
+
<line number="194" hits="1"/>
|
89
|
+
<line number="197" hits="1"/>
|
90
|
+
<line number="205" hits="1"/>
|
91
|
+
<line number="214" hits="1"/>
|
92
|
+
<line number="215" hits="1"/>
|
93
|
+
<line number="216" hits="1"/>
|
94
|
+
<line number="218" hits="1"/>
|
95
|
+
<line number="219" hits="1"/>
|
96
|
+
<line number="220" hits="1"/>
|
97
|
+
<line number="221" hits="1"/>
|
98
|
+
<line number="222" hits="1"/>
|
99
|
+
<line number="223" hits="1"/>
|
100
|
+
<line number="224" hits="1"/>
|
101
|
+
<line number="225" hits="1"/>
|
102
|
+
<line number="226" hits="1"/>
|
103
|
+
<line number="227" hits="1"/>
|
104
|
+
<line number="228" hits="1"/>
|
105
|
+
<line number="229" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
106
|
+
<line number="230" hits="1"/>
|
107
|
+
<line number="231" hits="1"/>
|
108
|
+
<line number="232" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
109
|
+
<line number="241" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
110
|
+
<line number="251" hits="1"/>
|
111
|
+
<line number="252" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
112
|
+
<line number="258" hits="1"/>
|
113
|
+
<line number="259" hits="1"/>
|
114
|
+
<line number="260" hits="1"/>
|
115
|
+
<line number="261" hits="1"/>
|
116
|
+
<line number="262" hits="1"/>
|
117
|
+
<line number="263" hits="1"/>
|
118
|
+
<line number="264" hits="1"/>
|
119
|
+
<line number="266" hits="1"/>
|
120
|
+
<line number="267" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
121
|
+
<line number="268" hits="1"/>
|
122
|
+
<line number="269" hits="1"/>
|
123
|
+
<line number="270" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
124
|
+
<line number="271" hits="1"/>
|
125
|
+
<line number="272" hits="1"/>
|
126
|
+
<line number="273" hits="1"/>
|
127
|
+
<line number="275" hits="1"/>
|
128
|
+
<line number="276" hits="1"/>
|
129
|
+
<line number="277" hits="1"/>
|
130
|
+
<line number="278" hits="1"/>
|
131
|
+
<line number="279" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
132
|
+
<line number="280" hits="1"/>
|
133
|
+
<line number="286" hits="1"/>
|
134
|
+
<line number="291" hits="1"/>
|
135
|
+
<line number="292" hits="1"/>
|
136
|
+
<line number="294" hits="1"/>
|
137
|
+
<line number="295" hits="1"/>
|
138
|
+
<line number="297" hits="1"/>
|
139
|
+
<line number="298" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
140
|
+
<line number="299" hits="1"/>
|
141
|
+
<line number="300" hits="1"/>
|
142
|
+
<line number="301" hits="1"/>
|
143
|
+
<line number="302" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
144
|
+
<line number="303" hits="1"/>
|
145
|
+
<line number="307" hits="1"/>
|
146
|
+
<line number="314" hits="1"/>
|
147
|
+
<line number="315" hits="1"/>
|
148
|
+
<line number="317" hits="1"/>
|
149
|
+
<line number="318" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
150
|
+
<line number="319" hits="1"/>
|
151
|
+
<line number="321" hits="1"/>
|
152
|
+
<line number="322" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
153
|
+
<line number="339" hits="1"/>
|
154
|
+
<line number="340" hits="1"/>
|
155
|
+
<line number="341" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
156
|
+
<line number="342" hits="1"/>
|
157
|
+
<line number="343" hits="1"/>
|
158
|
+
<line number="352" hits="1"/>
|
159
|
+
<line number="353" hits="1"/>
|
160
|
+
<line number="359" hits="1"/>
|
161
|
+
<line number="361" hits="1"/>
|
162
|
+
<line number="362" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
163
|
+
<line number="414" hits="1"/>
|
164
|
+
<line number="415" hits="1"/>
|
165
|
+
<line number="416" hits="1"/>
|
166
|
+
<line number="417" hits="1"/>
|
167
|
+
<line number="418" hits="1"/>
|
168
|
+
<line number="419" hits="1"/>
|
169
|
+
<line number="420" hits="1"/>
|
170
|
+
<line number="421" hits="1"/>
|
171
|
+
<line number="422" hits="1"/>
|
172
|
+
<line number="424" hits="1"/>
|
173
|
+
<line number="425" hits="1"/>
|
174
|
+
<line number="429" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
175
|
+
<line number="430" hits="1"/>
|
176
|
+
<line number="434" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
177
|
+
<line number="435" hits="1"/>
|
178
|
+
<line number="437" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
179
|
+
<line number="438" hits="1"/>
|
180
|
+
<line number="441" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
181
|
+
<line number="442" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
182
|
+
<line number="444" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
183
|
+
<line number="445" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
184
|
+
<line number="447" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
185
|
+
<line number="448" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
186
|
+
<line number="450" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
187
|
+
<line number="451" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
188
|
+
<line number="453" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
189
|
+
<line number="454" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
190
|
+
<line number="457" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
191
|
+
<line number="458" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
192
|
+
<line number="460" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
193
|
+
<line number="461" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
194
|
+
<line number="465" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
195
|
+
<line number="466" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
196
|
+
<line number="468" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
197
|
+
<line number="469" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
198
|
+
<line number="472" hits="1"/>
|
199
|
+
<line number="473" hits="1"/>
|
200
|
+
<line number="475" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
201
|
+
<line number="476" hits="1"/>
|
202
|
+
<line number="478" hits="1"/>
|
203
|
+
<line number="479" hits="1"/>
|
204
|
+
<line number="481" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
205
|
+
<line number="482" hits="1"/>
|
206
|
+
<line number="485" hits="1"/>
|
207
|
+
<line number="486" hits="1"/>
|
208
|
+
<line number="489" hits="1"/>
|
209
|
+
<line number="490" hits="1"/>
|
210
|
+
<line number="491" hits="1"/>
|
211
|
+
<line number="493" hits="1"/>
|
212
|
+
<line number="494" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
213
|
+
<line number="500" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
214
|
+
<line number="503" hits="1"/>
|
215
|
+
<line number="504" hits="1"/>
|
216
|
+
<line number="505" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
217
|
+
<line number="506" hits="1"/>
|
218
|
+
<line number="507" hits="1"/>
|
219
|
+
<line number="508" hits="1"/>
|
220
|
+
<line number="509" hits="1"/>
|
221
|
+
<line number="510" hits="1"/>
|
222
|
+
<line number="512" hits="1"/>
|
223
|
+
<line number="513" hits="1" branch="true" condition-coverage="100% (2/2)"/>
|
224
|
+
<line number="514" hits="1"/>
|
225
|
+
<line number="517" hits="1"/>
|
226
|
+
<line number="518" hits="1"/>
|
227
|
+
<line number="519" hits="1"/>
|
228
|
+
<line number="522" hits="1"/>
|
229
|
+
<line number="523" hits="1"/>
|
230
|
+
<line number="524" hits="1"/>
|
231
|
+
<line number="527" hits="1"/>
|
232
|
+
<line number="528" hits="1"/>
|
233
|
+
<line number="535" hits="1"/>
|
234
|
+
</lines>
|
235
|
+
</class>
|
236
|
+
</classes>
|
237
|
+
</package>
|
238
|
+
</packages>
|
239
|
+
</coverage>
|
@@ -14,6 +14,7 @@
|
|
14
14
|
# You should have received a copy of the GNU General Public License along
|
15
15
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
16
16
|
import datetime
|
17
|
+
import importlib.metadata
|
17
18
|
import json
|
18
19
|
import logging
|
19
20
|
import math
|
@@ -21,7 +22,6 @@ import traceback
|
|
21
22
|
import uuid
|
22
23
|
from warnings import warn
|
23
24
|
|
24
|
-
import pkg_resources
|
25
25
|
from sqlalchemy import (
|
26
26
|
and_,
|
27
27
|
between,
|
@@ -31,11 +31,14 @@ from sqlalchemy import (
|
|
31
31
|
DDL,
|
32
32
|
event,
|
33
33
|
ForeignKey,
|
34
|
+
func,
|
34
35
|
Integer,
|
35
36
|
not_,
|
36
37
|
or_,
|
38
|
+
select,
|
37
39
|
String,
|
38
40
|
Table,
|
41
|
+
text,
|
39
42
|
TypeDecorator,
|
40
43
|
Unicode,
|
41
44
|
UnicodeText,
|
@@ -61,6 +64,9 @@ except ImportError: # pragma: no cover
|
|
61
64
|
UniqueViolation = lookup_error("23505")
|
62
65
|
|
63
66
|
|
67
|
+
__version__ = importlib.metadata.version("datanommer.models")
|
68
|
+
|
69
|
+
|
64
70
|
log = logging.getLogger("datanommer")
|
65
71
|
|
66
72
|
maker = sessionmaker()
|
@@ -80,7 +86,7 @@ def init(uri=None, alembic_ini=None, engine=None, create=False):
|
|
80
86
|
raise ValueError("One of uri or engine must be specified")
|
81
87
|
|
82
88
|
if uri and not engine:
|
83
|
-
engine = create_engine(uri)
|
89
|
+
engine = create_engine(uri, future=True)
|
84
90
|
|
85
91
|
# We need to hang our own attribute on the sqlalchemy session to stop
|
86
92
|
# ourselves from initializing twice. That is only a problem if the code
|
@@ -90,11 +96,12 @@ def init(uri=None, alembic_ini=None, engine=None, create=False):
|
|
90
96
|
return
|
91
97
|
session._datanommer_initialized = True
|
92
98
|
|
93
|
-
|
99
|
+
maker.configure(bind=engine)
|
94
100
|
DeclarativeBase.query = session.query_property()
|
95
101
|
|
96
102
|
if create:
|
97
|
-
|
103
|
+
with engine.begin() as connection:
|
104
|
+
connection.execute(text("CREATE EXTENSION IF NOT EXISTS timescaledb"))
|
98
105
|
DeclarativeBase.metadata.create_all(engine)
|
99
106
|
# Loads the alembic configuration and generates the version table, with
|
100
107
|
# the most recent revision stamped as head
|
@@ -122,7 +129,7 @@ def add(message):
|
|
122
129
|
log.exception("Failed to parse sent-at timestamp value")
|
123
130
|
return
|
124
131
|
else:
|
125
|
-
sent_at = datetime.datetime.
|
132
|
+
sent_at = datetime.datetime.now(tz=datetime.timezone.utc)
|
126
133
|
|
127
134
|
# Workaround schemas misbehaving
|
128
135
|
try:
|
@@ -158,11 +165,6 @@ def add(message):
|
|
158
165
|
session.commit()
|
159
166
|
|
160
167
|
|
161
|
-
def source_version_default(context):
|
162
|
-
dist = pkg_resources.get_distribution("datanommer.models")
|
163
|
-
return dist.version
|
164
|
-
|
165
|
-
|
166
168
|
# https://docs.sqlalchemy.org/en/14/core/custom_types.html#marshal-json-strings
|
167
169
|
|
168
170
|
|
@@ -224,7 +226,7 @@ class Message(DeclarativeBase):
|
|
224
226
|
username = Column(Unicode)
|
225
227
|
crypto = Column(UnicodeText)
|
226
228
|
source_name = Column(Unicode, default="datanommer")
|
227
|
-
source_version = Column(Unicode, default=
|
229
|
+
source_version = Column(Unicode, default=lambda context: __version__)
|
228
230
|
msg = Column(JSONEncodedDict, nullable=False)
|
229
231
|
headers = Column(postgresql.JSONB(none_as_null=True))
|
230
232
|
users = relationship(
|
@@ -314,7 +316,7 @@ class Message(DeclarativeBase):
|
|
314
316
|
|
315
317
|
@classmethod
|
316
318
|
def from_msg_id(cls, msg_id):
|
317
|
-
return cls.
|
319
|
+
return session.execute(select(cls).where(cls.msg_id == msg_id)).scalar_one_or_none()
|
318
320
|
|
319
321
|
def as_dict(self, request=None):
|
320
322
|
return dict(
|
@@ -337,13 +339,12 @@ class Message(DeclarativeBase):
|
|
337
339
|
def as_fedora_message_dict(self):
|
338
340
|
headers = self.headers or {}
|
339
341
|
if "sent-at" not in headers:
|
340
|
-
headers["sent-at"] = self.timestamp.astimezone(
|
341
|
-
datetime.timezone.utc
|
342
|
-
).isoformat()
|
342
|
+
headers["sent-at"] = self.timestamp.astimezone(datetime.timezone.utc).isoformat()
|
343
343
|
return dict(
|
344
344
|
body=self.msg,
|
345
345
|
headers=headers,
|
346
346
|
id=self.msg_id,
|
347
|
+
priority=headers.get("priority", 0),
|
347
348
|
queue=None,
|
348
349
|
topic=self.topic,
|
349
350
|
)
|
@@ -353,6 +354,7 @@ class Message(DeclarativeBase):
|
|
353
354
|
"The __json__() method has been renamed to as_dict(), and will be removed "
|
354
355
|
"in the next major version",
|
355
356
|
DeprecationWarning,
|
357
|
+
stacklevel=2,
|
356
358
|
)
|
357
359
|
return self.as_dict(request)
|
358
360
|
|
@@ -419,69 +421,55 @@ class Message(DeclarativeBase):
|
|
419
421
|
not_topics = not_topics or []
|
420
422
|
contains = contains or []
|
421
423
|
|
422
|
-
|
424
|
+
Message = cls
|
425
|
+
query = select(Message)
|
423
426
|
|
424
427
|
# A little argument validation. We could provide some defaults in
|
425
428
|
# these mixed cases.. but instead we'll just leave it up to our caller.
|
426
429
|
if (start is not None and end is None) or (end is not None and start is None):
|
427
430
|
raise ValueError(
|
428
|
-
"Either both start and end must be specified "
|
429
|
-
"or neither must be specified"
|
431
|
+
"Either both start and end must be specified or neither must be specified"
|
430
432
|
)
|
431
433
|
|
432
434
|
if start and end:
|
433
|
-
query = query.
|
435
|
+
query = query.where(between(Message.timestamp, start, end))
|
434
436
|
|
435
437
|
if msg_id:
|
436
|
-
query = query.
|
438
|
+
query = query.where(Message.msg_id == msg_id)
|
437
439
|
|
438
440
|
# Add the four positive filters as necessary
|
439
441
|
if users:
|
440
|
-
query = query.
|
441
|
-
or_(*(Message.users.any(User.name == u) for u in users))
|
442
|
-
)
|
442
|
+
query = query.where(or_(*(Message.users.any(User.name == u) for u in users)))
|
443
443
|
|
444
444
|
if packages:
|
445
|
-
query = query.
|
446
|
-
or_(*(Message.packages.any(Package.name == p) for p in packages))
|
447
|
-
)
|
445
|
+
query = query.where(or_(*(Message.packages.any(Package.name == p) for p in packages)))
|
448
446
|
|
449
447
|
if categories:
|
450
|
-
query = query.
|
451
|
-
or_(*(Message.category == category for category in categories))
|
452
|
-
)
|
448
|
+
query = query.where(or_(*(Message.category == category for category in categories)))
|
453
449
|
|
454
450
|
if topics:
|
455
|
-
query = query.
|
451
|
+
query = query.where(or_(*(Message.topic == topic for topic in topics)))
|
456
452
|
|
457
453
|
if contains:
|
458
|
-
query = query.
|
459
|
-
or_(*(Message.msg.like(f"%{contain}%") for contain in contains))
|
460
|
-
)
|
454
|
+
query = query.where(or_(*(Message.msg.like(f"%{contain}%") for contain in contains)))
|
461
455
|
|
462
456
|
# And then the four negative filters as necessary
|
463
457
|
if not_users:
|
464
|
-
query = query.
|
465
|
-
not_(or_(*(Message.users.any(User.name == u) for u in not_users)))
|
466
|
-
)
|
458
|
+
query = query.where(not_(or_(*(Message.users.any(User.name == u) for u in not_users))))
|
467
459
|
|
468
460
|
if not_packs:
|
469
|
-
query = query.
|
461
|
+
query = query.where(
|
470
462
|
not_(or_(*(Message.packages.any(Package.name == p) for p in not_packs)))
|
471
463
|
)
|
472
464
|
|
473
465
|
if not_cats:
|
474
|
-
query = query.
|
475
|
-
not_(or_(*(Message.category == category for category in not_cats)))
|
476
|
-
)
|
466
|
+
query = query.where(not_(or_(*(Message.category == category for category in not_cats))))
|
477
467
|
|
478
468
|
if not_topics:
|
479
|
-
query = query.
|
480
|
-
not_(or_(*(Message.topic == topic for topic in not_topics)))
|
481
|
-
)
|
469
|
+
query = query.where(not_(or_(*(Message.topic == topic for topic in not_topics))))
|
482
470
|
|
483
471
|
# Finally, tag on our pagination arguments
|
484
|
-
total = query.count()
|
472
|
+
total = session.scalar(query.with_only_columns(func.count(Message.id)))
|
485
473
|
query = query.order_by(getattr(Message.timestamp, order)())
|
486
474
|
|
487
475
|
if not rows_per_page:
|
@@ -494,12 +482,11 @@ class Message(DeclarativeBase):
|
|
494
482
|
return total, page, query
|
495
483
|
else:
|
496
484
|
# Execute!
|
497
|
-
messages = query.all()
|
485
|
+
messages = session.scalars(query).all()
|
498
486
|
return total, pages, messages
|
499
487
|
|
500
488
|
|
501
489
|
class NamedSingleton:
|
502
|
-
|
503
490
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
504
491
|
name = Column(UnicodeText, index=True, unique=True)
|
505
492
|
|
@@ -513,8 +500,8 @@ class NamedSingleton:
|
|
513
500
|
if name in cls._cache:
|
514
501
|
# If we cache the instance, SQLAlchemy will run this query anyway because the instance
|
515
502
|
# will be from a different transaction. So just cache the id.
|
516
|
-
return
|
517
|
-
obj = cls.
|
503
|
+
return session.get(cls, cls._cache[name])
|
504
|
+
obj = session.execute(select(cls).where(cls.name == name)).scalar_one_or_none()
|
518
505
|
if obj is None:
|
519
506
|
obj = cls(name=name)
|
520
507
|
session.add(obj)
|
@@ -546,16 +533,3 @@ def _setup_hypertable(table_class):
|
|
546
533
|
|
547
534
|
|
548
535
|
_setup_hypertable(Message)
|
549
|
-
|
550
|
-
|
551
|
-
# Set the version
|
552
|
-
try: # pragma: no cover
|
553
|
-
import importlib.metadata
|
554
|
-
|
555
|
-
__version__ = importlib.metadata.version("datanommer.models")
|
556
|
-
except ImportError: # pragma: no cover
|
557
|
-
try:
|
558
|
-
__version__ = pkg_resources.get_distribution("datanommer.models").version
|
559
|
-
except pkg_resources.DistributionNotFound:
|
560
|
-
# The app is not installed, but the flask dev server can run it nonetheless.
|
561
|
-
__version__ = None
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import pytest
|
2
|
+
import sqlalchemy as sa
|
3
|
+
from pytest_postgresql import factories
|
4
|
+
from pytest_postgresql.janitor import DatabaseJanitor
|
5
|
+
from sqlalchemy.orm import scoped_session
|
6
|
+
|
7
|
+
import datanommer.models as dm
|
8
|
+
|
9
|
+
|
10
|
+
postgresql_proc = factories.postgresql_proc(
|
11
|
+
postgres_options="-c shared_preload_libraries=timescaledb -c timescaledb.telemetry_level=off",
|
12
|
+
)
|
13
|
+
|
14
|
+
|
15
|
+
@pytest.fixture(scope="session")
|
16
|
+
def datanommer_db_url(postgresql_proc):
|
17
|
+
return (
|
18
|
+
f"postgresql+psycopg2://{postgresql_proc.user}:@"
|
19
|
+
f"{postgresql_proc.host}:{postgresql_proc.port}"
|
20
|
+
f"/{postgresql_proc.dbname}"
|
21
|
+
)
|
22
|
+
|
23
|
+
|
24
|
+
@pytest.fixture()
|
25
|
+
def datanommer_db(postgresql_proc, datanommer_db_url):
|
26
|
+
with DatabaseJanitor(
|
27
|
+
user=postgresql_proc.user,
|
28
|
+
host=postgresql_proc.host,
|
29
|
+
port=postgresql_proc.port,
|
30
|
+
dbname=postgresql_proc.dbname,
|
31
|
+
# Don't use a template database
|
32
|
+
# template_dbname=postgresql_proc.template_dbname,
|
33
|
+
version=postgresql_proc.version,
|
34
|
+
):
|
35
|
+
engine = sa.create_engine(datanommer_db_url, future=True, poolclass=sa.pool.NullPool)
|
36
|
+
# Renew the global object, dm.init checks a custom attribute
|
37
|
+
dm.session = scoped_session(dm.maker)
|
38
|
+
dm.init(engine=engine, create=True)
|
39
|
+
yield engine
|
40
|
+
dm.session.close()
|
41
|
+
|
42
|
+
|
43
|
+
@pytest.fixture()
|
44
|
+
def datanommer_models(datanommer_db):
|
45
|
+
dm.User.clear_cache()
|
46
|
+
dm.Package.clear_cache()
|
47
|
+
yield dm.session
|
48
|
+
dm.session.rollback()
|