Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
engine
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Container registry
Model registry
Operate
Environments
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD 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
AURA
engine
Commits
97c89e4c
Commit
97c89e4c
authored
4 years ago
by
David Trattnig
Browse files
Options
Downloads
Patches
Plain Diff
Rename.
parent
b50e8190
No related branches found
No related tags found
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
api.py
+0
-417
0 additions, 417 deletions
api.py
aura.py
+0
-122
0 additions, 122 deletions
aura.py
with
0 additions
and
539 deletions
api.py
deleted
100644 → 0
+
0
−
417
View file @
b50e8190
#!/usr/bin/python3.7
#
# Aura Engine
#
# Copyright (C) 2020 David Trattnig <david.trattnig@subsquare.at>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import
logging
import
os
,
os
.
path
from
datetime
import
datetime
,
date
,
timedelta
from
flask
import
Flask
,
Response
from
flask_caching
import
Cache
from
flask_cors
import
CORS
from
flask_sqlalchemy
import
SQLAlchemy
from
flask_marshmallow
import
Marshmallow
from
marshmallow
import
Schema
,
fields
,
post_dump
from
flask_restful
import
Api
,
Resource
,
abort
from
apispec
import
APISpec
from
apispec.ext.marshmallow
import
MarshmallowPlugin
from
apispec_webframeworks.flask
import
FlaskPlugin
# import werkzeug
from
werkzeug.exceptions
import
HTTPException
,
default_exceptions
,
Aborter
from
modules.base.logger
import
AuraLogger
from
modules.base.config
import
AuraConfig
from
libraries.database.broadcasts
import
AuraDatabaseModel
,
Schedule
,
Playlist
,
PlaylistEntry
,
PlaylistEntryMetaData
,
TrackService
#
# Initialize the Aura Web App and API.
#
config
=
AuraConfig
()
app
=
Flask
(
__name__
,
static_url_path
=
''
,
static_folder
=
'
web/
'
)
# static_folder='contrib/aura-player/public/')
app
.
config
[
"
SQLALCHEMY_TRACK_MODIFICATIONS
"
]
=
False
app
.
config
[
"
SQLALCHEMY_DATABASE_URI
"
]
=
config
.
get_database_uri
()
app
.
config
[
"
CACHE_TYPE
"
]
=
"
simple
"
app
.
config
[
"
CACHE_DEFAULT_TIMEOUT
"
]
=
0
app
.
config
[
'
SEND_FILE_MAX_AGE_DEFAULT
'
]
=
0
cache
=
Cache
(
app
)
cors
=
CORS
(
app
,
resources
=
{
r
"
/*
"
:
{
"
origins
"
:
"
*
"
}})
# FIXME Update CORS for production use
db
=
SQLAlchemy
(
app
)
ma
=
Marshmallow
(
app
)
api
=
Api
(
app
)
#
# Werkzeug HTTP code mappings
#
class
NoDataAvailable
(
HTTPException
):
code
=
204
description
=
"
There is currently no content available.
"
default_exceptions
[
204
]
=
NoDataAvailable
abort
=
Aborter
()
class
EngineApi
:
"""
Provides the Aura Engine API services.
"""
config
=
None
api
=
None
logger
=
None
# trackservice_schema = None
def
__init__
(
self
,
config
,
api
):
"""
Initializes the API.
Args:
config (AuraConfig): The Engine configuration.
api (Api): The Flask restful API object.
"""
self
.
config
=
config
self
.
logger
=
AuraLogger
(
self
.
config
,
"
engine-api
"
)
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
self
.
api
=
api
# Generate HTML files
self
.
generate_html
(
"
web/templates/clock.html
"
,
"
web/clock.html
"
)
self
.
generate_html
(
"
web/templates/trackservice.html
"
,
"
web/trackservice.html
"
)
# API Spec
spec
.
components
.
schema
(
"
TrackService
"
,
schema
=
TrackServiceSchema
)
spec
.
components
.
schema
(
"
Report
"
,
schema
=
ReportSchema
)
spec
.
components
.
schema
(
"
Schedule
"
,
schema
=
ScheduleSchema
)
spec
.
components
.
schema
(
"
Clock
"
,
schema
=
ClockDataSchema
)
# TODO Generates HTML for specification
#self.logger.info(spec.to_yaml())
# Schema instances
EngineApi
.
trackservice_schema
=
TrackServiceSchema
(
many
=
True
)
EngineApi
.
track_schema
=
TrackServiceSchema
()
EngineApi
.
report_schema
=
ReportSchema
(
many
=
True
)
EngineApi
.
schedule_schema
=
ScheduleSchema
(
many
=
True
)
EngineApi
.
clockdata_schema
=
ClockDataSchema
()
# Define API routes
self
.
api
.
add_resource
(
TrackServiceResource
,
config
.
api_prefix
+
"
/trackservice/
"
)
self
.
api
.
add_resource
(
TrackResource
,
config
.
api_prefix
+
"
/trackservice/<int:track_id>
"
)
self
.
api
.
add_resource
(
CurrentTrackResource
,
config
.
api_prefix
+
"
/trackservice/current
"
)
self
.
api
.
add_resource
(
TracksByDayResource
,
config
.
api_prefix
+
"
/trackservice/date/<string:date_string>
"
)
self
.
api
.
add_resource
(
ReportResource
,
config
.
api_prefix
+
"
/report/<string:year_month>
"
)
self
.
api
.
add_resource
(
UpcomingSchedulesResource
,
config
.
api_prefix
+
"
/schedule/upcoming
"
)
self
.
api
.
add_resource
(
ClockDataResource
,
config
.
api_prefix
+
"
/clock
"
)
self
.
logger
.
info
(
"
Engine API routes successfully set!
"
)
# Static resources
@app.route
(
'
/app/trackservice
'
,
methods
=
[
'
GET
'
])
def
trackservice
():
content
=
open
(
os
.
path
.
join
(
"
web/
"
,
"
trackservice.html
"
))
return
Response
(
content
,
mimetype
=
"
text/html
"
)
# Static resources
@app.route
(
'
/app/clock
'
,
methods
=
[
'
GET
'
])
def
clock
():
content
=
open
(
os
.
path
.
join
(
"
web/
"
,
"
clock.html
"
))
return
Response
(
content
,
mimetype
=
"
text/html
"
)
def
generate_html
(
self
,
src_file
,
target_file
):
"""
Generates HTML based on the configuration options and templates.
Args:
src_file (String): The template file
target_file (String): The HTML file to be generated
"""
src_file
=
open
(
src_file
,
"
r
"
)
target_file
=
open
(
target_file
,
"
w
"
)
content
=
src_file
.
read
()
config_options
=
{
"
CONFIG-STATION-NAME
"
:
config
.
station_name
,
"
CONFIG-STATION-LOGO-URL
"
:
config
.
station_logo_url
,
"
CONFIG-STATION-LOGO-SIZE
"
:
config
.
station_logo_size
,
"
CONFIG-API-URL
"
:
config
.
exposed_api_url
}
for
key
,
value
in
config_options
.
items
():
content
=
content
.
replace
(
"
:::
"
+
key
+
"
:::
"
,
value
)
target_file
.
write
(
content
)
src_file
.
close
()
target_file
.
close
()
def
run
(
self
):
"""
Starts the API server.
"""
# Set debug=False if you want to use your native IDE debugger
self
.
api
.
app
.
run
(
port
=
self
.
config
.
api_port
,
debug
=
False
)
#
# API SPEC
#
spec
=
APISpec
(
title
=
"
Swagger API Specification for Aura Engine
"
,
version
=
"
1.0.0
"
,
openapi_version
=
"
3.0.2
"
,
plugins
=
[
FlaskPlugin
(),
MarshmallowPlugin
()],
)
#
# API SCHEMA
#
class
TrackServiceSchema
(
ma
.
Schema
):
class
Meta
:
fields
=
(
"
id
"
,
"
schedule.schedule_id
"
,
"
schedule.schedule_start
"
,
"
schedule.schedule_end
"
,
"
schedule.languages
"
,
"
schedule.type
"
,
"
schedule.category
"
,
"
schedule.topic
"
,
"
schedule.musicfocus
"
,
"
schedule.is_repetition
"
,
"
track
"
,
"
track_start
"
,
"
show
"
)
class
ClockDataSchema
(
ma
.
Schema
):
class
Meta
:
fields
=
(
"
current
"
,
"
next
"
,
"
track_id
"
,
"
track_start
"
,
"
track
"
)
class
ScheduleSchema
(
ma
.
Schema
):
class
Meta
:
fields
=
(
"
id
"
,
"
schedule_id
"
,
"
schedule_start
"
,
"
schedule
"
,
"
show_id
"
,
"
show_name
"
,
"
show_hosts
"
,
"
show_type
"
)
class
ReportSchema
(
ma
.
Schema
):
class
Meta
:
fields
=
(
"
id
"
,
"
schedule.schedule_id
"
,
"
schedule.schedule_start
"
,
"
schedule.schedule_end
"
,
"
schedule.languages
"
,
"
schedule.type
"
,
"
schedule.category
"
,
"
schedule.topic
"
,
"
schedule.musicfocus
"
,
"
schedule.is_repetition
"
,
"
schedule.show_id
"
,
"
schedule.show_name
"
,
"
schedule.show_hosts
"
,
"
schedule.show_type
"
,
"
schedule.show_funding_category
"
,
"
track
"
,
"
track_start
"
,
"
playlist_id
"
,
"
fallback_type
"
,
"
schedule_fallback_id
"
,
"
show_fallback_id
"
,
"
station_fallback_id
"
)
#
# API RESOURCES
#
class
TrackServiceResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
):
today
=
date
.
today
()
today
=
datetime
(
today
.
year
,
today
.
month
,
today
.
day
)
tracks
=
TrackService
.
select_by_day
(
today
)
return
EngineApi
.
trackservice_schema
.
dump
(
tracks
)
class
TrackResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
,
track_id
):
track
=
TrackService
.
select_one
(
track_id
)
return
EngineApi
.
track_schema
.
dump
(
track
)
class
ClockDataResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
):
item
=
TrackService
.
select_current
()
next_schedule
=
Schedule
.
select_upcoming
(
1
)
if
next_schedule
:
next_schedule
=
next_schedule
[
0
].
as_dict
()
next_schedule
[
"
playlist
"
]
=
None
else
:
next_schedule
=
{}
clockdata
=
{
"
track_id
"
:
item
.
id
,
"
track_start
"
:
item
.
track_start
,
"
track
"
:
item
.
track
,
"
current
"
:
{},
"
next
"
:
next_schedule
}
if
item
.
schedule
:
clockdata
[
"
current
"
]
=
item
.
schedule
.
as_dict
()
if
item
.
schedule
.
playlist
:
clockdata
[
"
current
"
][
"
playlist
"
]
=
item
.
schedule
.
playlist
[
0
].
as_dict
()
clockdata
[
"
current
"
][
"
show
"
]
=
item
.
show
return
EngineApi
.
clockdata_schema
.
dump
(
clockdata
)
class
CurrentTrackResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
):
track
=
TrackService
.
select_current
()
if
not
track
:
return
abort
(
204
)
# No content available
return
EngineApi
.
track_schema
.
dump
(
track
)
class
TracksByDayResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
,
date_string
):
date
=
datetime
.
strptime
(
date_string
,
"
%Y-%m-%d
"
)
self
.
logger
.
debug
(
"
Query track-service by day: %s
"
%
str
(
date
))
tracks
=
TrackService
.
select_by_day
(
date
)
if
not
tracks
:
return
abort
(
204
)
# No content available
return
EngineApi
.
trackservice_schema
.
dump
(
tracks
)
class
UpcomingSchedulesResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
):
now
=
datetime
.
now
()
self
.
logger
.
debug
(
"
Query upcoming schedules after %s
"
%
str
(
now
))
schedules
=
Schedule
.
select_upcoming
(
3
)
if
not
schedules
:
return
abort
(
204
)
# No content available
return
EngineApi
.
schedule_schema
.
dump
(
schedules
)
class
ReportResource
(
Resource
):
logger
=
None
def
__init__
(
self
):
self
.
logger
=
logging
.
getLogger
(
"
engine-api
"
)
def
get
(
self
,
year_month
):
year
=
int
(
year_month
.
split
(
"
-
"
)[
0
])
month
=
int
(
year_month
.
split
(
"
-
"
)[
1
])
first_day
=
datetime
(
year
,
month
,
1
)
next_month
=
first_day
.
replace
(
day
=
28
)
+
timedelta
(
days
=
4
)
next_month
-
timedelta
(
days
=
next_month
.
day
)
self
.
logger
.
debug
(
"
Query report for month: %s - %s
"
%
(
str
(
first_day
),
str
(
next_month
)))
report
=
TrackService
.
select_by_range
(
first_day
,
next_month
)
if
not
report
:
return
abort
(
204
)
# No content available
return
EngineApi
.
report_schema
.
dump
(
report
)
#
# Initialization calls
#
engine_api
=
EngineApi
(
config
,
api
)
if
__name__
==
"
__main__
"
:
engine_api
.
run
()
This diff is collapsed.
Click to expand it.
aura.py
deleted
100755 → 0
+
0
−
122
View file @
b50e8190
#!/usr/bin/python3.7
#
# Aura Engine
#
# Copyright (C) 2017-2020
# David Trattnig <david.trattnig@subsquare.at>
# Gottfried Gaisbauer <gottfried.gaisbauer@servus.at>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import
os
import
sys
import
signal
import
logging
import
unittest
import
meta
from
flask
import
Flask
from
flask_sqlalchemy
import
SQLAlchemy
# from sqlalchemy.ext.declarative import declarative_base
from
modules.base.logger
import
AuraLogger
from
modules.base.config
import
AuraConfig
from
modules.base.utils
import
EngineUtil
# from modules.monitoring.diskspace_watcher import DiskSpaceWatcher
config
=
AuraConfig
()
def
configure_flask
():
app
.
config
[
"
SQLALCHEMY_DATABASE_URI
"
]
=
config
.
get_database_uri
()
app
.
config
[
'
BABEL_DEFAULT_LOCALE
'
]
=
'
de
'
app
.
config
[
'
SQLALCHEMY_TRACK_MODIFICATIONS
'
]
=
False
# FIXME Instatiate SQLAlchemy without the need for Flask
app
=
Flask
(
__name__
)
configure_flask
()
DB
=
SQLAlchemy
(
app
)
# Base = declarative_base()
class
Aura
:
"""
Aura Class
The core of Aura Engine.
"""
logger
=
None
config
=
None
server
=
None
messenger
=
None
controller
=
None
scheduler
=
None
def
__init__
(
self
):
"""
Initializes the Engine Core.
"""
self
.
config
=
config
AuraLogger
(
self
.
config
)
self
.
logger
=
logging
.
getLogger
(
"
AuraEngine
"
)
def
startup
(
self
):
"""
Starts the Engine Core.
"""
from
modules.scheduling.scheduler
import
AuraScheduler
from
modules.core.engine
import
SoundSystem
from
modules.communication.redis.adapter
import
ServerRedisAdapter
# Check if the database has to be re-created
if
self
.
config
.
get
(
"
recreate_db
"
)
is
not
None
:
AuraScheduler
(
self
.
config
,
None
)
# Create scheduler and Liquidsoap communicator
self
.
soundsystem
=
SoundSystem
(
self
.
config
)
self
.
scheduler
=
AuraScheduler
(
self
.
config
,
self
.
soundsystem
)
# Create the Redis adapter
self
.
messenger
=
ServerRedisAdapter
(
self
.
config
)
self
.
messenger
.
scheduler
=
self
.
scheduler
self
.
messenger
.
soundsystem
=
self
.
soundsystem
# TODO Check if it's working / needed.
#self.diskspace_watcher = DiskSpaceWatcher(self.config, self.logger, self.soundsystem)
#self.diskspace_watcher.start()
# And finally wait for redis message / start listener thread
self
.
messenger
.
start
()
#
# START THE ENGINE
#
if
__name__
==
"
__main__
"
:
aura
=
Aura
()
if
len
(
sys
.
argv
)
>=
2
:
if
"
--use-test-data
"
in
sys
.
argv
:
aura
.
config
.
set
(
"
use_test_data
"
,
True
)
if
"
--recreate-database
"
in
sys
.
argv
:
aura
.
config
.
set
(
"
recreate_db
"
,
True
)
aura
.
startup
()
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