mirror of
				https://github.com/bspeice/elektricity
				synced 2025-11-04 02:00:27 -05:00 
			
		
		
		
	Refactoring work to make this a real program
This commit is contained in:
		
							
								
								
									
										18
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										18
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
								
							@ -13,23 +13,7 @@
 | 
			
		||||
    <ConfirmationsSetting value="0" id="Add" />
 | 
			
		||||
    <ConfirmationsSetting value="0" id="Remove" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe)" project-jdk-type="Python SDK">
 | 
			
		||||
  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe)" project-jdk-type="Python SDK">
 | 
			
		||||
    <output url="file://$PROJECT_DIR$/out" />
 | 
			
		||||
  </component>
 | 
			
		||||
  <component name="masterDetails">
 | 
			
		||||
    <states>
 | 
			
		||||
      <state key="ProjectJDKs.UI">
 | 
			
		||||
        <settings>
 | 
			
		||||
          <last-edited>Python 3.5.1 (C:\Users\Bradlee Speice\Anaconda3\python.exe)</last-edited>
 | 
			
		||||
          <splitter-proportions>
 | 
			
		||||
            <option name="proportions">
 | 
			
		||||
              <list>
 | 
			
		||||
                <option value="0.2" />
 | 
			
		||||
              </list>
 | 
			
		||||
            </option>
 | 
			
		||||
          </splitter-proportions>
 | 
			
		||||
        </settings>
 | 
			
		||||
      </state>
 | 
			
		||||
    </states>
 | 
			
		||||
  </component>
 | 
			
		||||
</project>
 | 
			
		||||
@ -1,12 +1,15 @@
 | 
			
		||||
# Format: (all args are passed to __init__ as kwargs
 | 
			
		||||
# Format (all args are passed to __init__ as kwargs:
 | 
			
		||||
#
 | 
			
		||||
# <mountpoint>:
 | 
			
		||||
#   class: <feed_class>
 | 
			
		||||
#   args:
 | 
			
		||||
#     key: value
 | 
			
		||||
subfactory-show:
 | 
			
		||||
  package: bassdrive
 | 
			
		||||
  class: BassdriveFeed
 | 
			
		||||
server:
 | 
			
		||||
  port: 10000
 | 
			
		||||
 | 
			
		||||
podcasts:
 | 
			
		||||
  subfactory-show:
 | 
			
		||||
    class: podcasters.BassdriveFeed
 | 
			
		||||
    args:
 | 
			
		||||
      url: http://archives.bassdrivearchive.com/1%20-%20Monday/Subfactory%20Show%20-%20DJ%20Spim/
 | 
			
		||||
      logo: http://www.bassdrive.com/img/radio_schedule_entries/image/original/subfactory-web-add-56.jpg
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,9 @@
 | 
			
		||||
<module type="PYTHON_MODULE" version="4">
 | 
			
		||||
  <component name="NewModuleRootManager" inherit-compiler-output="true">
 | 
			
		||||
    <exclude-output />
 | 
			
		||||
    <content url="file://$MODULE_DIR$" />
 | 
			
		||||
    <content url="file://$MODULE_DIR$">
 | 
			
		||||
      <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
 | 
			
		||||
    </content>
 | 
			
		||||
    <orderEntry type="inheritedJdk" />
 | 
			
		||||
    <orderEntry type="sourceFolder" forTests="false" />
 | 
			
		||||
  </component>
 | 
			
		||||
 | 
			
		||||
@ -1,30 +0,0 @@
 | 
			
		||||
"""
 | 
			
		||||
Given a configuration file, set up everything needed to kick
 | 
			
		||||
off the server.
 | 
			
		||||
"""
 | 
			
		||||
from importlib import import_module
 | 
			
		||||
import yaml
 | 
			
		||||
from pyramid.config import Configurator
 | 
			
		||||
from os.path import expanduser, join
 | 
			
		||||
 | 
			
		||||
# Needed for import_module call
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
import modules
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_configuration(conf=None) -> Configurator:
 | 
			
		||||
    if conf is None:
 | 
			
		||||
        conf = join(expanduser('~'), '.repodrc')
 | 
			
		||||
 | 
			
		||||
    with open(conf) as conf_file:
 | 
			
		||||
        conf_dict = yaml.load(conf_file)
 | 
			
		||||
        server_conf = Configurator()
 | 
			
		||||
        for mountpoint, feed in conf_dict.items():
 | 
			
		||||
            feed_package = import_module('modules.' + feed['package'])
 | 
			
		||||
            feed_class = getattr(feed_package, feed['class'])
 | 
			
		||||
            feed_instance = feed_class(**feed['args'])
 | 
			
		||||
 | 
			
		||||
            server_conf.add_route(mountpoint, '/' + mountpoint + '/')
 | 
			
		||||
            server_conf.add_view(feed_instance.view, route_name=mountpoint)
 | 
			
		||||
 | 
			
		||||
        return server_conf
 | 
			
		||||
@ -1,10 +0,0 @@
 | 
			
		||||
from wsgiref.simple_server import make_server
 | 
			
		||||
from conf_parser import build_configuration
 | 
			
		||||
 | 
			
		||||
def start_server():
 | 
			
		||||
    app = build_configuration().make_wsgi_app()
 | 
			
		||||
    server = make_server('0.0.0.0', 8080, app)
 | 
			
		||||
    server.serve_forever()
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    start_server()
 | 
			
		||||
							
								
								
									
										38
									
								
								src/conf_parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/conf_parser.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
"""
 | 
			
		||||
Given a configuration file, set up everything needed to kick
 | 
			
		||||
off the server.
 | 
			
		||||
"""
 | 
			
		||||
from importlib import import_module
 | 
			
		||||
 | 
			
		||||
import yaml
 | 
			
		||||
from pyramid.config import Configurator
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_configurator(podcasts: dict) -> Configurator:
 | 
			
		||||
    server_conf = Configurator()
 | 
			
		||||
    for mountpoint, feed in podcasts:
 | 
			
		||||
        package, class_name = feed['class'].rsplit('.', 1)
 | 
			
		||||
        feed_package = import_module(package)
 | 
			
		||||
        feed_class = getattr(feed_package, class_name)
 | 
			
		||||
        feed_instance = feed_class(**feed['args'])
 | 
			
		||||
 | 
			
		||||
        server_conf.add_route(mountpoint, '/' + mountpoint + '/')
 | 
			
		||||
        server_conf.add_view(feed_instance.view, route_name=mountpoint)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_configuration_text(file_str: str) -> (dict, Configurator):
 | 
			
		||||
    conf_dict = yaml.load(file_str)
 | 
			
		||||
 | 
			
		||||
    server_opts = conf_dict.get('server', None)
 | 
			
		||||
    podcasts = build_configurator(conf_dict['podcasts'])
 | 
			
		||||
    return server_opts, podcasts
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def build_configuration(file_name) -> (dict, Configurator):
 | 
			
		||||
    try:
 | 
			
		||||
        with open(file_name) as conf_file:
 | 
			
		||||
            return build_configuration_text(conf_file.read())
 | 
			
		||||
    except FileNotFoundError:
 | 
			
		||||
        print("Could not locate configuration file " +
 | 
			
		||||
              "(does {} exist?)".format(file_name))
 | 
			
		||||
        raise
 | 
			
		||||
@ -11,6 +11,7 @@ class BasePodcast():
 | 
			
		||||
        "Return a list of all episodes, in descending date order"
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    # noinspection PyUnusedLocal
 | 
			
		||||
    def view(self, request):
 | 
			
		||||
        fg = self.build_feed()
 | 
			
		||||
        response = Response(fg.rss_str(pretty=True))
 | 
			
		||||
@ -1,22 +1,26 @@
 | 
			
		||||
"""
 | 
			
		||||
Podcast provider for the Bassdrive Archives
 | 
			
		||||
"""
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from html.parser import HTMLParser
 | 
			
		||||
from urllib.parse import unquote
 | 
			
		||||
 | 
			
		||||
import requests
 | 
			
		||||
from feedgen.feed import FeedGenerator
 | 
			
		||||
 | 
			
		||||
from podcast import BasePodcast
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from pytz import UTC
 | 
			
		||||
 | 
			
		||||
from podcasters.base import BasePodcast
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BassdriveParser(HTMLParser):
 | 
			
		||||
    def error(self, message):
 | 
			
		||||
        return super().error(message)
 | 
			
		||||
 | 
			
		||||
    record_link_text = False
 | 
			
		||||
    link_url = ''
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        # noinspection PyArgumentList
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.links = []
 | 
			
		||||
 | 
			
		||||
@ -62,7 +66,6 @@ class BassdriveFeed(BasePodcast):
 | 
			
		||||
 | 
			
		||||
        # And turn them into something usable
 | 
			
		||||
        fg = FeedGenerator()
 | 
			
		||||
        #fg.load_extension('podcast')
 | 
			
		||||
        fg.id(self.url)
 | 
			
		||||
        fg.title(self.title)
 | 
			
		||||
        fg.description(self.title)
 | 
			
		||||
@ -79,8 +82,8 @@ class BassdriveFeed(BasePodcast):
 | 
			
		||||
            fe.enclosure(self.url + link[1], 0, 'audio/mpeg')
 | 
			
		||||
 | 
			
		||||
            # Bassdrive always uses date strings of
 | 
			
		||||
            # [yyyy.mm.dd] with 0 padding, so that
 | 
			
		||||
            # makes our lives easy
 | 
			
		||||
            # [yyyy.mm.dd] with 0 padding on days and months,
 | 
			
		||||
            # so that makes our lives easy
 | 
			
		||||
            date_start = link[0].find('[')
 | 
			
		||||
            date_str = link[0][date_start:date_start+12]
 | 
			
		||||
            published = datetime.strptime(date_str, '[%Y.%m.%d]')
 | 
			
		||||
							
								
								
									
										38
									
								
								src/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/server.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
			
		||||
import argparse
 | 
			
		||||
from wsgiref.simple_server import make_server
 | 
			
		||||
from conf_parser import build_configuration
 | 
			
		||||
from os.path import expanduser, join
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# noinspection PyUnresolvedReferences
 | 
			
		||||
def start_server(cmd_args: dict):
 | 
			
		||||
    try:
 | 
			
		||||
        server_conf, configurator = build_configuration(cmd_args.configuration)
 | 
			
		||||
        app = configurator.make_wsgi_app()
 | 
			
		||||
        server = make_server(cmd_args.host, cmd_args.port, app)
 | 
			
		||||
 | 
			
		||||
        server.serve_forever()
 | 
			
		||||
    except FileNotFoundError:
 | 
			
		||||
        print("Unable to find configuration file. Does {} exist?"
 | 
			
		||||
              .format(cmd_args.configuration))
 | 
			
		||||
    except AttributeError:
 | 
			
		||||
        print("Unable to parse configuration file. Is {} a valid YML file?"
 | 
			
		||||
              .format(cmd_args.configuration))
 | 
			
		||||
    except KeyError:
 | 
			
		||||
        print('Unable to parse configuration file. Is there a `podcasts`'
 | 
			
		||||
              'section?')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    default_rc = join(expanduser('~'), '.repodrc')
 | 
			
		||||
    parser = argparse.ArgumentParser()
 | 
			
		||||
    parser.add_argument('--verbose', help='Run server in verbose mode')
 | 
			
		||||
    parser.add_argument('--port', type=int, default=10000,
 | 
			
		||||
                        help='Port to use when starting the server')
 | 
			
		||||
    parser.add_argument('--host', type=str, default='0.0.0.0',
 | 
			
		||||
                        help='Host address to start the server')
 | 
			
		||||
    parser.add_argument('--configuration', type=str, default=default_rc,
 | 
			
		||||
                        help='Configuration file to start the server')
 | 
			
		||||
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
    start_server(args)
 | 
			
		||||
							
								
								
									
										0
									
								
								src/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/tests/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										12
									
								
								src/tests/test_build_configurator.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tests/test_build_configurator.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
from unittest import TestCase
 | 
			
		||||
import conf_parser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestBuild_configurator(TestCase):
 | 
			
		||||
    def test_build_configurator(self):
 | 
			
		||||
        try:
 | 
			
		||||
            # noinspection PyTypeChecker
 | 
			
		||||
            conf_parser.build_configurator(None)
 | 
			
		||||
            self.fail("Must have dictionary to set up configurator")
 | 
			
		||||
        except TypeError:
 | 
			
		||||
            pass
 | 
			
		||||
		Reference in New Issue
	
	Block a user