Skip to content
Snippets Groups Projects
api.py 8.87 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    #
    # 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/>.
    
    # Meta
    __version__ = '0.0.1'
    __license__ = "GNU Affero General Public License (AGPL) Version 3"
    __version_info__ = (0, 0, 1)
    __author__ = 'David Trattnig <david.trattnig@subsquare.at>'
    
    
    import logging
    
    David Trattnig's avatar
    David Trattnig committed
    import os, os.path
    
    David Trattnig's avatar
    David Trattnig committed
    from datetime                       import datetime, date, timedelta
    
    from flask                          import Flask, Response
    
    David Trattnig's avatar
    David Trattnig committed
    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
    
    from apispec                        import APISpec
    from apispec.ext.marshmallow        import MarshmallowPlugin
    from apispec_webframeworks.flask    import FlaskPlugin
    
    
    from libraries.base.logger          import AuraLogger
    from libraries.base.config          import AuraConfig
    from libraries.database.broadcasts  import AuraDatabaseModel, Schedule, Playlist, PlaylistEntry, PlaylistEntryMetaData, TrackService
    
    
    David Trattnig's avatar
    David Trattnig committed
    
    
    
    
    #
    # Initialize the Aura Web App and API.
    #
    
    config = AuraConfig()
    
    app = Flask(__name__,
        static_url_path='', 
        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": "*"}})
    
    db = SQLAlchemy(app)
    ma = Marshmallow(app)
    api = Api(app)
    
    
    
    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
    
    
            spec.components.schema("TrackService", schema=TrackServiceSchema)
            
    
            # Schema instances
            EngineApi.trackservice_schema = TrackServiceSchema(many=True)
            EngineApi.track_schema = TrackServiceSchema()
    
    David Trattnig's avatar
    David Trattnig committed
            EngineApi.report_schema = ReportSchema(many=True)
    
            EngineApi.schedule_schema = ScheduleSchema(many=True)
    
            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>")
    
    David Trattnig's avatar
    David Trattnig committed
            self.api.add_resource(UpcomingSchedulesResource,    config.api_prefix + "/schedule/upcoming")
    
    David Trattnig's avatar
    David Trattnig committed
    
    
            self.logger.info("Engine API routes successfully set!")
    
    
            # Static resources
            @app.route('/trackservice', methods=['GET'])
            def trackservice():
                content = open(os.path.join("contrib/aura-player/public/", "index.html"))
                return Response(content, mimetype="text/html")
    
    
    
            # Print the API Spec
            # TODO Generates HTML for specification
            # self.logger.info(spec.to_dict())
            # self.logger.info(spec.to_yaml())
    
        def run(self):
            """
            Starts the API server.
            """
    
            # TODO Extend to provide production-grade app 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",
    
    David Trattnig's avatar
    David Trattnig committed
    
                "schedule.show_id",
                "schedule.show_name",
                "schedule.show_hosts",
    
    
    class ScheduleSchema(ma.Schema):
        class Meta:
            fields = (
                "id", 
                "schedule_id",
                "schedule_start",
                "schedule",
    
                "show_id",
                "show_name",
                "show_hosts",
                "show_type",
                )
    
    
    
    
    David Trattnig's avatar
    David Trattnig committed
    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 CurrentTrackResource(Resource):
        logger = None
        
        def __init__(self):
            self.logger = logging.getLogger("engine-api")
    
        def get(self, track_id):
            track = TrackService.select_current()
            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)
            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)
            return EngineApi.schedule_schema.dump(schedules)
    
    
    
    David Trattnig's avatar
    David Trattnig committed
    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)
            return EngineApi.report_schema.dump(report)
    
    
    
    
    
    # Initialization calls
    
    if __name__ == "__main__":
        engine_api = EngineApi(config, api)
        engine_api.run()