Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
AossTower
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Model registry
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
MetObs
AossTower
Commits
09633f6c
Commit
09633f6c
authored
10 years ago
by
Bruce Flynn
Browse files
Options
Downloads
Patches
Plain Diff
Moved some code to MetObsCommon
parent
13b80690
No related branches found
No related tags found
No related merge requests found
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
aosstower/model.py
+0
-148
0 additions, 148 deletions
aosstower/model.py
scripts/make_database.py
+5
-4
5 additions, 4 deletions
scripts/make_database.py
setup.py
+1
-8
1 addition, 8 deletions
setup.py
with
6 additions
and
160 deletions
aosstower/model.py
+
0
−
148
View file @
09633f6c
import
os
import
os
import
re
import
sys
from
datetime
import
datetime
,
timedelta
from
datetime
import
datetime
,
timedelta
import
rrdtool
import
rrdtool
import
numpy
as
np
from
zope.interface
import
implementer
from
metobs.data
import
wind_vector_degrees
,
to_unix_timestamp
from
metobs.data
import
wind_vector_degrees
,
to_unix_timestamp
from
metobscommon
import
interface
class
ModelError
(
Exception
):
"""
Base class for model errors.
"""
class
WrapErrors
(
object
):
"""
Class wrapper to catch exceptions and properly re-raise them such that
the only exceptions to propagate are `ModelError`s. Essentially, this
prevents anyone from having to import rrdtool lib.
"""
def
__init__
(
self
,
*
exceptions
):
self
.
exceptions
=
exceptions
def
__call__
(
self
,
cls
):
def
_wrap
(
fcn
):
def
wrapped
(
*
args
,
**
kwargs
):
try
:
return
fcn
(
*
args
,
**
kwargs
)
except
self
.
exceptions
as
err
:
traceback
=
sys
.
exc_info
()[
2
]
raise
ModelError
,
str
(
err
),
traceback
wrapped
.
__doc__
=
fcn
.
__doc__
return
wrapped
for
name
in
dir
(
cls
):
value
=
getattr
(
cls
,
name
)
if
not
name
.
startswith
(
'
_
'
)
and
hasattr
(
value
,
'
__call__
'
):
setattr
(
cls
,
name
,
_wrap
(
value
))
return
cls
def
initialize
(
filepath
,
start
=
None
,
days
=
365
,
data_interval
=
5
):
def
initialize
(
filepath
,
start
=
None
,
days
=
365
,
data_interval
=
5
):
...
@@ -74,114 +37,3 @@ def initialize(filepath, start=None, days=365, data_interval=5):
...
@@ -74,114 +37,3 @@ def initialize(filepath, start=None, days=365, data_interval=5):
'
RRA:AVERAGE:0.5:{:d}:105120
'
.
format
(
300
/
data_interval
),
'
RRA:AVERAGE:0.5:{:d}:105120
'
.
format
(
300
/
data_interval
),
# 30 minute
# 30 minute
'
RRA:AVERAGE:0.5:{:d}:17520
'
.
format
(
1800
/
data_interval
))
'
RRA:AVERAGE:0.5:{:d}:17520
'
.
format
(
1800
/
data_interval
))
@WrapErrors
(
rrdtool
.
error
)
@implementer
(
interface
.
Model
)
class
RrdModel
(
object
):
"""
Model for storing the Level0 uncalibrated data for non-scientific
purposes, such as web-widgets.
"""
def
__init__
(
self
,
filepath
):
self
.
_filepath
=
filepath
self
.
_averages
=
tuple
()
self
.
_datasets
=
None
@property
def
datasets
(
self
):
"""
Get dataset names available in the database.
"""
if
self
.
_datasets
is
None
:
datasets
=
set
()
info
=
rrdtool
.
info
(
self
.
_filepath
)
for
key
in
info
.
keys
():
match
=
re
.
match
(
'
^ds\[(.*)\]
'
,
key
)
if
not
match
:
continue
datasets
.
add
(
match
.
groups
()[
0
])
self
.
_datasets
=
tuple
(
sorted
(
datasets
))
return
self
.
_datasets
def
averaging_intervals
(
self
):
"""
Lazy load averaging intervals from database.
"""
if
not
self
.
_averages
:
averages
=
set
()
info
=
rrdtool
.
info
(
self
.
_filepath
)
for
key
in
info
.
keys
():
if
key
.
startswith
(
'
rra
'
)
and
key
.
endswith
(
'
pdp_per_row
'
):
averages
.
add
(
int
(
info
[
key
]
*
info
[
'
step
'
]))
self
.
_averages
=
tuple
(
sorted
(
averages
))
return
self
.
_averages
def
_format_data
(
self
,
stamp
,
data
):
"""
Format data for insert into RRD returning a template string and data
line appropriate for arguments to rrdupdate.
"""
validkeys
=
set
(
self
.
datasets
).
intersection
(
data
.
keys
())
if
not
validkeys
:
raise
ModelError
(
"
No valid data keys provided
"
,
data
)
tmpl
=
'
:
'
.
join
(
validkeys
)
values
=
'
:
'
.
join
([
str
(
data
[
k
])
for
k
in
validkeys
])
values
=
'
{:d}@{}
'
.
format
(
to_unix_timestamp
(
stamp
),
values
)
return
tmpl
,
values
def
add_record
(
self
,
stamp
,
record
):
"""
Add a single record to the database, where a record is a dict like
object with keys for each dataset. Additional keys are ignored.
"""
# Normalize to data interval
utime
=
to_unix_timestamp
(
stamp
)
data_interval
=
min
(
self
.
averaging_intervals
())
stamp
=
datetime
.
utcfromtimestamp
(
utime
-
utime
%
data_interval
)
tmpl
,
data
=
self
.
_format_data
(
stamp
,
dict
(
record
))
rrdtool
.
update
(
self
.
_filepath
,
'
--template=%s
'
%
tmpl
,
data
)
def
get_slice
(
self
,
start
,
end
,
names
=
None
,
average
=
5
):
"""
Get a slice of data from the database.
:param start: Start time as datetime
:param end: Inclusive end time as datetime
:param names: Names to query for, defaults to all available, see ``datasets``
:param average: Averaging interval supported by the database, see ``averaging_intervals``.
"""
if
average
not
in
self
.
averaging_intervals
():
raise
ValueError
(
"
Invalid average:%d
"
,
average
)
names
=
names
or
self
.
datasets
[:]
if
isinstance
(
start
,
datetime
):
start
=
to_unix_timestamp
(
start
)
if
isinstance
(
end
,
datetime
):
end
=
to_unix_timestamp
(
end
)
# normalize request times to averaging interval
start
-=
start
%
average
end
-=
end
%
average
# we always get all the data, no matter what was requested
range
,
columns
,
rawdata
=
rrdtool
.
fetch
(
self
.
_filepath
,
'
AVERAGE
'
,
'
-r {:d}
'
.
format
(
average
),
'
-s {:d}
'
.
format
(
start
),
'
-e {:d}
'
.
format
(
end
))
src_data
=
np
.
array
(
rawdata
)
# NaN filled matrix of shape big enough for the request names
dst_data
=
np
.
zeros
((
src_data
.
shape
[
0
],
len
(
names
)))
*
float
(
'
nan
'
)
# get only the columns we're interested in
for
dst_idx
,
name
in
enumerate
(
names
):
if
name
in
columns
:
dst_data
[:,
dst_idx
]
=
src_data
[:,
columns
.
index
(
name
)]
# recompose the wind direction if asked for
elif
name
==
'
wind_dir
'
:
east
=
src_data
[:,
self
.
datasets
.
index
(
'
winddir_east
'
)].
astype
(
np
.
float64
)
north
=
src_data
[:,
self
.
datasets
.
index
(
'
winddir_north
'
)].
astype
(
np
.
float64
)
dst_data
[:,
dst_idx
]
=
wind_vector_degrees
(
east
,
north
)
# generate column of times for the req average interval
times
=
np
.
array
([
np
.
arange
(
start
,
end
+
average
,
average
)])
return
np
.
concatenate
((
times
.
T
,
dst_data
),
axis
=
1
)
This diff is collapsed.
Click to expand it.
scripts/make_database.py
+
5
−
4
View file @
09633f6c
...
@@ -6,8 +6,9 @@ import logging
...
@@ -6,8 +6,9 @@ import logging
from
datetime
import
datetime
from
datetime
import
datetime
from
metobs.data
import
wind_vector_components
from
metobs.data
import
wind_vector_components
from
metobscommon.model
import
RrdModel
from
aosstower.record
import
Record
,
LineParseError
from
aosstower.record
import
Record
,
LineParseError
from
aosstower
import
model
as
m
from
aosstower
.model
import
initialize
LOG
=
logging
LOG
=
logging
...
@@ -24,15 +25,15 @@ if __name__ == '__main__':
...
@@ -24,15 +25,15 @@ if __name__ == '__main__':
parser
.
add_argument
(
'
-d
'
,
'
--db-days
'
,
type
=
int
,
default
=
365
,
parser
.
add_argument
(
'
-d
'
,
'
--db-days
'
,
type
=
int
,
default
=
365
,
help
=
'
Size of DB in days
'
)
help
=
'
Size of DB in days
'
)
parser
.
add_argument
(
'
-i
'
,
dest
=
'
files
'
,
type
=
argparse
.
FileType
(
'
r
'
),
parser
.
add_argument
(
'
-i
'
,
dest
=
'
files
'
,
type
=
argparse
.
FileType
(
'
r
'
),
help
=
"
L
ist of time sorted input data files
"
)
help
=
"
File containing l
ist of time sorted input data files
"
)
args
=
parser
.
parse_args
()
args
=
parser
.
parse_args
()
logging
.
basicConfig
(
level
=
logging
.
INFO
)
logging
.
basicConfig
(
level
=
logging
.
INFO
)
assert
not
os
.
path
.
exists
(
args
.
outdb
)
assert
not
os
.
path
.
exists
(
args
.
outdb
)
m
.
initialize
(
args
.
outdb
,
args
.
db_start
,
days
=
args
.
db_days
)
initialize
(
args
.
outdb
,
args
.
db_start
,
days
=
args
.
db_days
)
rrd
=
m
.
RrdModel
(
args
.
outdb
)
rrd
=
RrdModel
(
args
.
outdb
)
LOG
.
info
(
"
initilized %s
"
,
args
.
outdb
)
LOG
.
info
(
"
initilized %s
"
,
args
.
outdb
)
if
args
.
files
is
None
:
if
args
.
files
is
None
:
...
...
This diff is collapsed.
Click to expand it.
setup.py
+
1
−
8
View file @
09633f6c
try
:
from
setuptools
import
setup
,
find_packages
from
setuptools
import
setup
,
find_packages
except
ImportError
:
from
ez_setup
import
use_setuptools
use_setuptools
()
from
setuptools
import
setup
,
find_packages
setup
(
setup
(
name
=
'
AossTower
'
,
name
=
'
AossTower
'
,
...
@@ -11,9 +6,7 @@ setup(
...
@@ -11,9 +6,7 @@ setup(
description
=
'
UW AOSS Rooftop Instrument Group Met Tower
'
,
description
=
'
UW AOSS Rooftop Instrument Group Met Tower
'
,
url
=
'
http://metobs.ssec.wisc.edu
'
,
url
=
'
http://metobs.ssec.wisc.edu
'
,
install_requires
=
[
install_requires
=
[
'
python-rrdtool
'
,
'
numpy
'
,
'
numpy
'
,
'
metobs.data>=0.4a
'
,
'
MetObsCommon>=0.1dev
'
'
MetObsCommon>=0.1dev
'
],
],
dependency_links
=
[
'
http://larch.ssec.wisc.edu/cgi-bin/repos.cgi
'
],
dependency_links
=
[
'
http://larch.ssec.wisc.edu/cgi-bin/repos.cgi
'
],
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment