Skip to content

Commit 6fcb024

Browse files
committed
#Note: v0.4.7
Several changes (see RELEASE_NOTES for details)
1 parent abda8ff commit 6fcb024

File tree

18 files changed

+577
-138
lines changed

18 files changed

+577
-138
lines changed

Dockerfile

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
FROM ubuntu:22.04
2+
3+
# Install system dependencies
4+
RUN apt-get update && apt-get install -y build-essential python3-pip virtualenv \
5+
python3-venv sqlite3 libsqlite3-dev sqlcipher libsqlcipher-dev git
6+
7+
# Set user, group, and create home directory
8+
ARG user=appuser
9+
ARG group=appuser
10+
ARG uid=1000
11+
ARG gid=1000
12+
RUN groupadd -g ${gid} ${group}
13+
RUN useradd -u ${uid} -g ${group} -s /bin/sh -m ${user}
14+
15+
# Aliases for application and working directories
16+
ENV appdir=/home/${user}/app
17+
ENV workdir=/home/${user}/work
18+
19+
WORKDIR ${appdir}
20+
21+
# Copy project files to the application directory
22+
COPY aware aware
23+
COPY pyproject.toml .
24+
COPY MANIFEST.in .
25+
26+
ENV PYTHONDONTWRITEBYTECODE 1
27+
ENV PYTHONUNBUFFERED 1
28+
29+
# Create a new virtual environment, install dependencies, and AWARE
30+
RUN python3 -m venv .venv && . .venv/bin/activate && python -m pip install -U pip wheel setuptools .
31+
32+
# Create entrypoint for application
33+
WORKDIR ${workdir}
34+
35+
ENTRYPOINT [ "/bin/bash", "-c", "exec ${appdir}/.venv/bin/python -m aware \"${@}\"", "--" ]
36+
37+
# Optional arguments (-t means run Telegram bot)
38+
39+
CMD ["-t"]

RELEASE_NOTES

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,24 @@ In this minor release:
6262
- Use default naming convention for table constraints for easier migration
6363
- Define declarative base in SQLAlchemy 2.0 style
6464

65+
## v0.4.8
66+
67+
- Fixed incorrect plot safe filename in plotting
68+
- Fixed absent default value for VOEvent parameters
69+
- Fixed survey name in finding chart from DSS2 to DSS2 R
70+
- Fixed Kafka client configuration (in test mode, auto.commit is disabled, and group.id
71+
is empty; in production mode group.id is uniquely set at each run),
72+
73+
- Added type hint for config options
74+
- Added new script for visibility plots
75+
- Updated find chart plot (added compass)
76+
- Updated visibility plot (main yaxis is altitude now; the Moon phase, and separation angle)
77+
- Restored alert rejection functionality (a new event matching previously rejected one,
78+
will be also rejected; all matched alerts with currently rejected one, will be marked
79+
as rejected in the database)
80+
- Added cache support for queries to SkyView, telescope list retrieval, subscribers querying,
81+
and for displaying available telescopes
82+
6583

6684
# v0.3.0
6785

aware/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
__version__ = ("0", "4", "7")
1+
__version__ = ("0", "4", "8")
22
__strversion__ = "{}.{}.{}".format(__version__)

aware/app.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from queue import Queue
66
from threading import Lock, Thread
77
from typing import Any, Callable, Sequence
8+
import uuid
89

910
from .config import dev
1011
from .consumer.main import ConsumeLoop, prepare_consumer
@@ -23,15 +24,16 @@ def import_tg_hook() -> Callable[[tuple[Any], dict[str, Any]], Thread]:
2324

2425

2526
CONSUMER_CONFIG = {
26-
"group.id": "lvk" if not dev.value else "lvk-test",
27+
"group.id": "" if dev.value else uuid.uuid4().hex,
2728
# Infinitely read messages in development mode since the beginning of
2829
# the partition
29-
"auto.offset.reset": "earliest" if dev.get_value() else "latest",
30-
"enable.auto.commit": True,
30+
"auto.offset.reset": "earliest" if dev.value else "latest",
31+
"enable.auto.commit": False if dev.value else True,
3132
# Set maximum idle time out between reading topic records to 1 day
3233
# (in msec) to prevent application maximum poll interval exceeded
3334
"max.poll.interval.ms": 86_400_000,
3435
}
36+
# CONSUMER_CONFIG = {}
3537

3638

3739
class Application:

aware/cache.py

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,105 @@
11
"""
22
Author: Nicolai Pankov (colinsergesen@gmail.com)
33
cache.py (c) 2023
4-
Desc: description
4+
Desc: Caching functions
55
Created: 2023-03-05
6-
Modified: 2023-03-06
6+
Modified: 2024-02-22
77
"""
88

99
from __future__ import annotations
10+
from typing import Any
1011

11-
from diskcache import Cache, JSONDisk, Disk
12+
from diskcache import Cache, DEFAULT_SETTINGS, Disk
1213
from .config import CfgOption
14+
from .logger import log
15+
from sqlite3 import Error
16+
17+
18+
__all__ = ["cache_value"]
1319

1420

1521
cache_dir = CfgOption("cache_dir", ".cache", str)
22+
timeout = CfgOption("timeout", 60, float)
23+
expire = CfgOption("expire", 86400, float)
24+
25+
26+
def __access_cache():
27+
"""
28+
Access the cache via calling this function, do not use global `Cache` object
29+
since it may be not created due to SQL errors (e.g. database is blocked)
30+
"""
31+
try:
32+
cache = Cache(cache_dir.get_value(), timeout=timeout.value)
33+
except Error as e:
34+
log.debug("Error getting cache database: %s", e)
35+
36+
class EmptyCache:
37+
def __init__(self, *args, **kwargs): ...
38+
39+
def set(self, *args, **kwargs): ...
40+
41+
def get(self, *args, **kwargs): ...
42+
43+
def __enter__(self): return self
44+
45+
def __exit__(self, *args, **kwargs): ...
46+
47+
cache = EmptyCache()
48+
49+
return cache
50+
51+
52+
def write_cache(
53+
key: str,
54+
value: Any,
55+
tag: str = "",
56+
expire: float = expire.value,
57+
retry: bool = True,
58+
):
59+
global __access_cache
60+
with __access_cache() as cache:
61+
# Additional arguments to cache set
62+
kws = {}
63+
64+
# File object
65+
if hasattr(value, "read"):
66+
kws["read"] = True
67+
68+
if tag is None:
69+
kws["tag"] = key.title()
70+
71+
try:
72+
cache.set(key, value, expire=expire, retry=retry, **kws)
73+
except Exception as e:
74+
if isinstance(e, KeyboardInterrupt):
75+
raise
76+
else:
77+
log.error("Cache entry %s is not writable %s", key, e)
78+
79+
80+
def read_cache(
81+
key: str,
82+
) -> Any:
83+
global __access_cache
84+
value = None
85+
with __access_cache() as cache:
86+
try:
87+
value = cache.get(key)
88+
except Exception as e:
89+
if isinstance(e, KeyboardInterrupt):
90+
raise
91+
else:
92+
log.error("Cache entry %s is not readable %s", key, e)
93+
94+
return value
95+
1696

17-
cache = Cache(cache_dir.get_value())
97+
def pop_cache(key: str):
98+
try:
99+
with __access_cache() as cache:
100+
cache.pop(key)
101+
except Exception as e:
102+
if isinstance(e, KeyboardInterrupt):
103+
raise
104+
else:
105+
log.error("Cache entry %s is not expirable %s", key, e)

aware/config.py

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
import os
1010
import sys
1111
from pathlib import Path
12-
from typing import Any, Callable, Mapping, Sequence, TypeVar
12+
from typing import Any, Callable, Mapping, Sequence, TypeVar, Generic
1313

1414
import dotenv
1515
import yaml
1616

17+
import warnings
18+
1719

1820
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
1921
ABS_DIR = os.path.abspath(BASE_DIR)
@@ -60,7 +62,7 @@ def load_config(
6062
__T = TypeVar("__T")
6163

6264

63-
class CfgOption:
65+
class CfgOption(Generic[__T]):
6466
"""
6567
YAML config option.
6668
@@ -74,15 +76,15 @@ class CfgOption:
7476
a function that processes and validates the value type
7577
"""
7678

77-
def __init__(self, name: str, value: Any, typ: Callable[[Any], Any]) -> None:
79+
def __init__(self, name: str, value: Any, typ: Callable[..., __T]) -> None:
7880
self._name = name
7981
self._value = value
8082
self.typ = typ
8183

8284
# Get caller filename here or it will always return filename where is the base
8385
# class located
8486
self.__filename = inspect.currentframe().f_back.f_code.co_filename
85-
87+
8688
def __get_aware_pkg_root(self) -> str:
8789
"""
8890
Get the path to the AWARE root directory
@@ -125,7 +127,14 @@ def section(self) -> str:
125127
except IndexError:
126128
return ""
127129

128-
def get_value(self) -> Any:
130+
def get_value(self) -> __T:
131+
warnings.warn(
132+
(
133+
"get_value() considered deprecated in next releases, use value "
134+
"property instead"
135+
),
136+
DeprecationWarning,
137+
)
129138
global cfg
130139
names = self.name.split(".")
131140

@@ -138,12 +147,10 @@ def get_value(self) -> Any:
138147
return self.typ(val)
139148
else:
140149
return self.typ(self._value)
141-
142150

143151
@property
144-
def value(self) -> Any:
152+
def value(self) -> __T:
145153
return self.get_value()
146-
147154

148155
def _get_val_from_nested_dict(
149156
self, d: Mapping[Any, Any], names: Sequence[str]
@@ -157,11 +164,11 @@ def _get_val_from_nested_dict(
157164
return d
158165

159166
return None
160-
167+
161168

162169
def set_config_option_value(name: str, val: Any, typ: Callable = str):
163170
global cfg
164-
171+
165172
# The option is in the submodule
166173
names = name.split(".")
167174
if len(names) > 1:
@@ -182,8 +189,14 @@ def set_config_option_value(name: str, val: Any, typ: Callable = str):
182189
cfg[name] = typ(val)
183190

184191

192+
# Development switch
185193
dev = CfgOption("dev", False, bool)
194+
195+
# load .env files with defined environment variables, if available
186196
dot_env_path = CfgOption("dot_env_path", ".env", str)
187-
if dev.get_value():
197+
if dot_env_path.get_value():
188198
if not dotenv.load_dotenv(dotenv_path=dot_env_path.get_value(), verbose=True):
189-
raise FileNotFoundError(".env file not found")
199+
print(
200+
f"No environment variables found in {dot_env_path.get_value()}",
201+
file=sys.stderr,
202+
)

0 commit comments

Comments
 (0)