udata-hydra 2.0.0.dev2730__tar.gz → 2.0.0.dev2867__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.
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/PKG-INFO +1 -1
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/pyproject.toml +12 -12
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/analysis/csv.py +1 -1
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/app.py +3 -6
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/config_default.toml +7 -5
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/routes/resources.py +0 -1
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/__init__.py +1 -0
- udata_hydra-2.0.0.dev2867/udata_hydra/utils/auth.py +72 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/README.md +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/analysis/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/analysis/errors.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/analysis/helpers.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/analysis/resource.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/cli.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/context.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/crawl.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/db/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/db/check.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/db/resource.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/logger.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/csv/20221205_initial_up_rev1.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/csv/20230130_drop_migrations.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/csv/20230206_datetime_aware.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20221205_initial_up_rev1.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20221206_rev1_up_rev2.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20221206_rev2_up_rev3.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20221208_rev3_up_rev4.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20221208_rev4_up_rev5.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230119_rev5_up_rev6.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230121_rev6_up_rev7.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230121_rev7_up_rev8.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230130_drop_migrations.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230206_datetime_aware.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230515_rev8_up_rev9.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20230606_rev9_up_rev10.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/migrations/main/20231102_drop_csv_analysis.sql +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/routes/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/routes/checks.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/routes/status.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/schemas/__init__.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/schemas/check.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/schemas/resource.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/app_version.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/csv.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/file.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/http.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/minio.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/parquet.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/queue.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/reader.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/utils/timer.py +0 -0
- {udata_hydra-2.0.0.dev2730 → udata_hydra-2.0.0.dev2867}/udata_hydra/worker.py +0 -0
|
@@ -9,7 +9,7 @@ readme = "README.md"
|
|
|
9
9
|
|
|
10
10
|
[tool.poetry]
|
|
11
11
|
name = "udata-hydra"
|
|
12
|
-
version = "2.0.0.
|
|
12
|
+
version = "2.0.0.dev2867"
|
|
13
13
|
description = "Async crawler and parsing service for data.gouv.fr"
|
|
14
14
|
authors = ["Opendata Team <opendatateam@data.gouv.fr>"]
|
|
15
15
|
license = "MIT"
|
|
@@ -17,27 +17,27 @@ readme = "README.md"
|
|
|
17
17
|
|
|
18
18
|
[tool.poetry.dependencies]
|
|
19
19
|
python = "^3.9"
|
|
20
|
+
aiocontextvars = "^0.2.2"
|
|
20
21
|
aiohttp = "^3.10.3"
|
|
21
22
|
asyncpg = "^0.29.0"
|
|
23
|
+
coloredlogs = "^15.0.1"
|
|
24
|
+
csv-detective = "0.7.2.dev800"
|
|
25
|
+
dateparser = "^1.1.7"
|
|
22
26
|
humanfriendly = "^10.0"
|
|
23
27
|
marshmallow = "^3.14.1"
|
|
24
28
|
minicli = "^0.5.3"
|
|
25
|
-
|
|
29
|
+
minio = "7.2.7"
|
|
30
|
+
pyarrow = "16.1.0"
|
|
31
|
+
python-dateutil = "^2.8.2"
|
|
26
32
|
python-magic = "^0.4.25"
|
|
33
|
+
progressist = "^0.1.0"
|
|
27
34
|
redis = "^4.1.4"
|
|
28
|
-
sentry-sdk = "^2.10.0"
|
|
29
|
-
aiocontextvars = "^0.2.2"
|
|
30
|
-
coloredlogs = "^15.0.1"
|
|
31
35
|
rq = "^1.11.1"
|
|
32
|
-
|
|
36
|
+
sentry-sdk = "^2.10.0"
|
|
37
|
+
sqlalchemy = "^1.4.46"
|
|
33
38
|
str2float = "^0.0.9"
|
|
34
39
|
str2bool = "^1.1"
|
|
35
|
-
|
|
36
|
-
dateparser = "^1.1.7"
|
|
37
|
-
python-dateutil = "^2.8.2"
|
|
38
|
-
csv-detective = "0.7.2.dev800"
|
|
39
|
-
pyarrow = "16.1.0"
|
|
40
|
-
minio = "7.2.7"
|
|
40
|
+
toml = "^0.10.2"
|
|
41
41
|
|
|
42
42
|
[tool.poetry.group.dev.dependencies]
|
|
43
43
|
aiohttp-devtools = "^1.0.post0"
|
|
@@ -74,7 +74,7 @@ RESERVED_COLS = ("__id", "tableoid", "xmin", "cmin", "xmax", "cmax", "ctid")
|
|
|
74
74
|
minio_client = MinIOClient()
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
async def notify_udata(check_id:
|
|
77
|
+
async def notify_udata(check_id: int, table_name: str) -> None:
|
|
78
78
|
"""Notify udata of the result of a parsing"""
|
|
79
79
|
check = await Check.get(check_id)
|
|
80
80
|
resource_id = check["resource_id"]
|
|
@@ -4,6 +4,7 @@ from aiohttp import web
|
|
|
4
4
|
|
|
5
5
|
from udata_hydra import context
|
|
6
6
|
from udata_hydra.routes import routes
|
|
7
|
+
from udata_hydra.utils import token_auth_middleware
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
async def app_factory() -> web.Application:
|
|
@@ -14,16 +15,12 @@ async def app_factory() -> web.Application:
|
|
|
14
15
|
if "pool" in app:
|
|
15
16
|
await app["pool"].close()
|
|
16
17
|
|
|
17
|
-
app = web.Application()
|
|
18
|
+
app = web.Application(middlewares=[token_auth_middleware(exclude_methods=("GET",))])
|
|
18
19
|
app.add_routes(routes)
|
|
19
20
|
app.on_startup.append(app_startup)
|
|
20
21
|
app.on_cleanup.append(app_cleanup)
|
|
21
22
|
return app
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
def run():
|
|
25
|
-
web.run_app(app_factory(), path=os.environ.get("HYDRA_APP_SOCKET_PATH"))
|
|
26
|
-
|
|
27
|
-
|
|
28
25
|
if __name__ == "__main__":
|
|
29
|
-
|
|
26
|
+
web.run_app(app_factory(), path=os.environ.get("HYDRA_APP_SOCKET_PATH"))
|
|
@@ -12,6 +12,8 @@ TESTING = false
|
|
|
12
12
|
MAX_POOL_SIZE = 50
|
|
13
13
|
USER_AGENT = "udata-hydra/1.0"
|
|
14
14
|
|
|
15
|
+
API_TOKEN = "to-change"
|
|
16
|
+
|
|
15
17
|
# -- crawler settings -- #
|
|
16
18
|
|
|
17
19
|
CATALOG_URL = "https://www.data.gouv.fr/fr/datasets/r/4babf5f2-6a9c-45b5-9144-ca5eae6a7a6d"
|
|
@@ -30,8 +32,8 @@ NO_BACKOFF_DOMAINS = [
|
|
|
30
32
|
]
|
|
31
33
|
# max number of _completed_ requests per domain per period
|
|
32
34
|
BACKOFF_NB_REQ = 180
|
|
33
|
-
BACKOFF_PERIOD = 360
|
|
34
|
-
COOL_OFF_PERIOD = 86400
|
|
35
|
+
BACKOFF_PERIOD = 360 # in seconds
|
|
36
|
+
COOL_OFF_PERIOD = 86400 # 1 day to cool off when we've messed up
|
|
35
37
|
# ids of the resources that are too large to be processed normally
|
|
36
38
|
# but that we want to have anyway
|
|
37
39
|
LARGE_RESOURCES_EXCEPTIONS = [
|
|
@@ -50,9 +52,9 @@ SLEEP_BETWEEN_BATCHES = 60
|
|
|
50
52
|
# max download filesize in bytes (100 MB)
|
|
51
53
|
MAX_FILESIZE_ALLOWED.csv = 104857600
|
|
52
54
|
MAX_FILESIZE_ALLOWED.csvgz = 104857600
|
|
53
|
-
MAX_FILESIZE_ALLOWED.xls = 52428800
|
|
54
|
-
MAX_FILESIZE_ALLOWED.xlsx = 13107200
|
|
55
|
-
MAX_FILESIZE_ALLOWED.ods = 10485760
|
|
55
|
+
MAX_FILESIZE_ALLOWED.xls = 52428800 # /2
|
|
56
|
+
MAX_FILESIZE_ALLOWED.xlsx = 13107200 # /8
|
|
57
|
+
MAX_FILESIZE_ALLOWED.ods = 10485760 # /10
|
|
56
58
|
|
|
57
59
|
|
|
58
60
|
CSV_ANALYSIS = true
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from typing import Coroutine, Tuple
|
|
3
|
+
|
|
4
|
+
from aiohttp import web
|
|
5
|
+
|
|
6
|
+
from udata_hydra import config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _is_exclude(request, exclude: Tuple[str]) -> bool:
|
|
10
|
+
for pattern in exclude:
|
|
11
|
+
if re.fullmatch(pattern, request.path):
|
|
12
|
+
return True
|
|
13
|
+
return False
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def token_auth_middleware(
|
|
17
|
+
request_property: str = "user",
|
|
18
|
+
auth_scheme: str = "Bearer",
|
|
19
|
+
exclude_routes: Tuple[str] = tuple(),
|
|
20
|
+
exclude_methods: Tuple[str] = tuple(),
|
|
21
|
+
) -> Coroutine:
|
|
22
|
+
"""Checks a auth token and adds a user in request.
|
|
23
|
+
|
|
24
|
+
Token auth middleware that checks the "Authorization" http header for token and, if the token in the requet headers is valid, then middleware adds the user to request with key that contain the "request_property" variable, else it will raise an HTTPForbiddenexception.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
request_property (str, optional): Key for save in request object.
|
|
28
|
+
Defaults to 'user'.
|
|
29
|
+
auth_scheme (str, optional): Prefix for value in "Authorization" header.
|
|
30
|
+
Defaults to 'Bearer'.
|
|
31
|
+
exclude_routes: (Tuple, optional): Tuple of pathes that will be excluded.
|
|
32
|
+
Defaults to empty tuple.
|
|
33
|
+
exclude_methods(Tuple, optional): Tuple of http methods that will be
|
|
34
|
+
excluded. Defaults to empty tuple.
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
web.HTTPUnauthorized: If "Authorization" token is missing.
|
|
38
|
+
web.HTTPForbidden: Wrong token, schema or header.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Coroutine: Aiohttp middleware.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@web.middleware
|
|
45
|
+
async def middleware(request, handler):
|
|
46
|
+
if _is_exclude(request, exclude_routes) or request.method in exclude_methods:
|
|
47
|
+
return await handler(request)
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
scheme, token = request.headers["Authorization"].strip().split(" ")
|
|
51
|
+
except KeyError:
|
|
52
|
+
raise web.HTTPUnauthorized(
|
|
53
|
+
reason="Missing authorization header",
|
|
54
|
+
)
|
|
55
|
+
except ValueError:
|
|
56
|
+
raise web.HTTPForbidden(
|
|
57
|
+
reason="Invalid authorization header",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if auth_scheme.lower() != scheme.lower():
|
|
61
|
+
raise web.HTTPForbidden(
|
|
62
|
+
reason="Invalid token scheme",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
if token == config.API_TOKEN:
|
|
66
|
+
request[request_property] = {"username": "admin"}
|
|
67
|
+
else:
|
|
68
|
+
raise web.HTTPForbidden(reason="Invalid authentication token")
|
|
69
|
+
|
|
70
|
+
return await handler(request)
|
|
71
|
+
|
|
72
|
+
return middleware
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|