howler-api 2.12.0.dev282__py3-none-any.whl → 2.12.0.dev286__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.
- howler/cronjobs/view_cleanup.py +88 -0
- howler/odm/models/config.py +16 -0
- {howler_api-2.12.0.dev282.dist-info → howler_api-2.12.0.dev286.dist-info}/METADATA +1 -1
- {howler_api-2.12.0.dev282.dist-info → howler_api-2.12.0.dev286.dist-info}/RECORD +6 -5
- {howler_api-2.12.0.dev282.dist-info → howler_api-2.12.0.dev286.dist-info}/WHEEL +0 -0
- {howler_api-2.12.0.dev282.dist-info → howler_api-2.12.0.dev286.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from typing import Any, List
|
|
4
|
+
|
|
5
|
+
from apscheduler.schedulers.base import BaseScheduler
|
|
6
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
7
|
+
from pytz import timezone
|
|
8
|
+
|
|
9
|
+
from howler.common.logging import get_logger
|
|
10
|
+
from howler.config import DEBUG, config
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__file__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def execute():
|
|
16
|
+
"""Delete any pinned views that no longer exist"""
|
|
17
|
+
from howler.common.loader import datastore
|
|
18
|
+
|
|
19
|
+
# Initialize datastore
|
|
20
|
+
ds = datastore()
|
|
21
|
+
# fetch the first result from user ds (needed to initialize total)
|
|
22
|
+
result = ds.user.search("*:*", rows=250, fl="*")
|
|
23
|
+
total_user_count = result["total"]
|
|
24
|
+
user_list: List[Any] = result["items"]
|
|
25
|
+
# Do the same thing for the views
|
|
26
|
+
result = ds.view.search("*:*", rows=250)
|
|
27
|
+
total_view_count = result["total"]
|
|
28
|
+
view_list: List[Any] = result["items"]
|
|
29
|
+
view_ids: List[str] = []
|
|
30
|
+
|
|
31
|
+
# Collect all views
|
|
32
|
+
while len(view_list) < total_view_count:
|
|
33
|
+
view_list.extend(ds.view.search("*:*", rows=250, offset=len(user_list)))
|
|
34
|
+
|
|
35
|
+
# Collect all users
|
|
36
|
+
while len(user_list) < total_user_count:
|
|
37
|
+
user_list.extend(ds.user.search("*:*", rows=250, offset=len(user_list)))
|
|
38
|
+
|
|
39
|
+
for view in view_list:
|
|
40
|
+
view_ids.append(view["view_id"])
|
|
41
|
+
|
|
42
|
+
# Iterate over each user to see if the dashboard contains invalid entries (deleted views)
|
|
43
|
+
for user in user_list:
|
|
44
|
+
valid_entries = []
|
|
45
|
+
# No views/analytics saved to the dashboard? Skip it
|
|
46
|
+
if user["dashboard"] == []:
|
|
47
|
+
continue
|
|
48
|
+
for dashboard_entry in user["dashboard"]:
|
|
49
|
+
if dashboard_entry["type"] != "view" or (
|
|
50
|
+
dashboard_entry["type"] == "view" and dashboard_entry["entry_id"] in view_ids
|
|
51
|
+
):
|
|
52
|
+
valid_entries.append(dashboard_entry)
|
|
53
|
+
# If the length of valid entries is less than the current dashboard, one or more pins are invalid
|
|
54
|
+
if len(valid_entries) < len(user["dashboard"]):
|
|
55
|
+
# set the user dashboard to valid entries
|
|
56
|
+
user["dashboard"] = valid_entries
|
|
57
|
+
# update the user
|
|
58
|
+
ds.user.save(user["uname"], user)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def setup_job(sched: BaseScheduler):
|
|
62
|
+
"""Initialize the view cleanup job"""
|
|
63
|
+
if not config.system.view_cleanup.enabled:
|
|
64
|
+
if not DEBUG or config.system.type == "production":
|
|
65
|
+
logger.warning("view cleanup cronjob disabled! This is not recommended for a production settings.")
|
|
66
|
+
|
|
67
|
+
return
|
|
68
|
+
|
|
69
|
+
logger.debug(f"Initializing view cleanup cronjob with cron {config.system.view_cleanup.crontab}")
|
|
70
|
+
|
|
71
|
+
if DEBUG:
|
|
72
|
+
_kwargs: dict[str, Any] = {"next_run_time": datetime.now()}
|
|
73
|
+
else:
|
|
74
|
+
_kwargs = {}
|
|
75
|
+
|
|
76
|
+
if sched.get_job("view_cleanup"):
|
|
77
|
+
logger.debug("view cleanup job already running!")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
sched.add_job(
|
|
81
|
+
id="view_cleanup",
|
|
82
|
+
func=execute,
|
|
83
|
+
trigger=CronTrigger.from_crontab(
|
|
84
|
+
config.system.view_cleanup.crontab, timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto"))
|
|
85
|
+
),
|
|
86
|
+
**_kwargs,
|
|
87
|
+
)
|
|
88
|
+
logger.debug("Initialization complete")
|
howler/odm/models/config.py
CHANGED
|
@@ -250,10 +250,26 @@ class Retention(BaseModel):
|
|
|
250
250
|
)
|
|
251
251
|
|
|
252
252
|
|
|
253
|
+
class ViewCleanup(BaseModel):
|
|
254
|
+
enabled: bool = Field(
|
|
255
|
+
default=True,
|
|
256
|
+
description=(
|
|
257
|
+
"Whether to enable the view cleanup. If enabled, views pinned "
|
|
258
|
+
"to the dashboard that no longer exist in the backend will be cleared."
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
crontab: str = Field(
|
|
262
|
+
default="0 0 * * *",
|
|
263
|
+
description="The crontab that denotes how often to run the view_cleanup job",
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
253
267
|
class System(BaseModel):
|
|
254
268
|
type: Literal["production", "staging", "development"] = Field(default="development", description="Type of system")
|
|
255
269
|
retention: Retention = Retention()
|
|
256
270
|
"Retention Configuration"
|
|
271
|
+
view_cleanup: ViewCleanup = ViewCleanup()
|
|
272
|
+
"View Cleanup Configuration"
|
|
257
273
|
|
|
258
274
|
|
|
259
275
|
class UI(BaseModel):
|
|
@@ -51,6 +51,7 @@ howler/config.py,sha256=amwf4FXJ1ZKeooHqPlC_F691tg6Yaj4osKn_ExiUSS0,2128
|
|
|
51
51
|
howler/cronjobs/__init__.py,sha256=GEhNsxPGATumlroMa-g6z5Dt9yy0QT93s3uuNC-GwIM,909
|
|
52
52
|
howler/cronjobs/retention.py,sha256=I-vXXFo2W5eHkJ-xQuywU2-KfM9xqgii9Y99Qd9E-Vk,1777
|
|
53
53
|
howler/cronjobs/rules.py,sha256=ZK3FmLsEF34-3vQzXkc4J6Y0P5-JSawMSnAkH9o-PeE,10406
|
|
54
|
+
howler/cronjobs/view_cleanup.py,sha256=ULWLR1uFcRemRgkEDrMqmBamHiE0dgDvu49VK0qDkuw,3076
|
|
54
55
|
howler/datastore/README.md,sha256=ekWl1YJSrHlZpU5PgBkPEzPWjdbTdav6Rd2P0ccIesw,4758
|
|
55
56
|
howler/datastore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
57
|
howler/datastore/bulk.py,sha256=VfolZfiaBD4ZTK3j6IVVVq4GMjVXb5elrsGwwM_nONE,2829
|
|
@@ -96,7 +97,7 @@ howler/odm/models/assemblyline.py,sha256=_wcTiX3A6bhA2SGlK9tDF0v-uwLpIabXE8j2Fw9
|
|
|
96
97
|
howler/odm/models/aws.py,sha256=pJVadJqubdgT27riCfp7bEKVP4XsMZB0ZUnKAbmCMd0,895
|
|
97
98
|
howler/odm/models/azure.py,sha256=o7MZMMo9jh1SB8xXCajl_YSKP2nnnWsjx_DPT6LnQKg,710
|
|
98
99
|
howler/odm/models/cbs.py,sha256=onUiJOGUxK3iy_-4XkGGwHxFiFq9Td_p59Kum4XaR-w,1366
|
|
99
|
-
howler/odm/models/config.py,sha256=
|
|
100
|
+
howler/odm/models/config.py,sha256=WBjMuqWY7XYt4Gx7uWFpEJz0760yhr91Iw8bQZTBas4,16629
|
|
100
101
|
howler/odm/models/dossier.py,sha256=Ob2qROrG2-DYzmVo2XVe4NJ8HjWGCoRAu2gPo6p9XGU,1244
|
|
101
102
|
howler/odm/models/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
103
|
howler/odm/models/ecs/agent.py,sha256=idSooyFCLuQAB7_RyEWTYW4-x9w5a3wpy2ct_-EDRQs,713
|
|
@@ -193,7 +194,7 @@ howler/utils/path.py,sha256=DfOU4i4zSs4wchHoE8iE7aWVLkTxiC_JRGepF2hBYBk,690
|
|
|
193
194
|
howler/utils/socket_utils.py,sha256=nz1SklC9xBHUSfHyTJjpq3mbozX1GDf01WzdGxfaUII,2212
|
|
194
195
|
howler/utils/str_utils.py,sha256=HE8Hqh2HlOLaj16w0H9zKOyDJLp-f1LQ50y_WeGZaEk,8389
|
|
195
196
|
howler/utils/uid.py,sha256=p9dsqyvZ-lpiAuzZWCPCeEM99kdk0Ly9czf04HNdSuw,1341
|
|
196
|
-
howler_api-2.12.0.
|
|
197
|
-
howler_api-2.12.0.
|
|
198
|
-
howler_api-2.12.0.
|
|
199
|
-
howler_api-2.12.0.
|
|
197
|
+
howler_api-2.12.0.dev286.dist-info/METADATA,sha256=MbQthk8FsH8UL6YzJzVHbgZMpl3Lu_AbYpzjszV1Dhg,2815
|
|
198
|
+
howler_api-2.12.0.dev286.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
199
|
+
howler_api-2.12.0.dev286.dist-info/entry_points.txt,sha256=Lu9SBGvwe0wczJHmc-RudC24lmQk7tv3ZBXon9RIihg,259
|
|
200
|
+
howler_api-2.12.0.dev286.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|