RadioCo documentation

Overview

RadioCo is a broadcasting radio recording scheduling system. RadioCo has been intended to provide a solution for a wide range of broadcast projects, from community to public and commercial stations.

Here are a few of the key features:

  • designed to work with any web browser
  • drag and drop scheduling calendar interface
  • live shows can be recorded and published automatically
  • complete authentication system (user accounts, groups, permissions)
  • ...and much more

Note

This software is not for live streaming

Release Notes

This document refers to version 3.0

Table of contents

Installation Guide

The pages in this section of the documentation are designed to help you get started quickly and show how easy it is to work with RadioCo

The guides follow a logical progression and build on each other, so it’s recommended to work through them in the order presented here.

Installing web application

This tutorial is written for Python 2.7 and Ubuntu 12.04 or later.

Installing on Ubuntu

The easiest way of installing the app is using Docker engine

Follow the installation steps

To allow RadioCo to generate correct dates it’s necessary to set the timezone variable:

  1. Find your timezone in this list
  2. Go to the config folder (radioco/configs/base) and create if not exists a local_settings.py in the same directory than settings.py
  3. Add to the local settings the variable timezone, for example: TIME_ZONE = “Europe/Madrid”

Open a terminal and introduce the following commands:

sudo apt-get install git-core python-dev python-pip

Next, download the project and cd into it:

git clone https://github.com/iago1460/django-radio
cd django-radio

Install the python invoke library:

pip install invoke==0.14.0

Execute the next command to deploy the app in docker, this step take some time:

inv quickstart
Testing

Let’s verify your installation

Now that the server’s running, visit http://127.0.0.1:8000/

Warning

Don’t use this server in anything resembling a production environment.

Using RadioCo on production

The Internet is a hostile environment. Before deploying this project, you should take some time to review your settings, with security, performance, and operations in mind. Keep in mind this critical settings.

RadioCo provides a staging environment, safer than the previous one but still insecure, use at your own risk.

inv docker.build -e staging docker.run -e staging docker.setup -e staging

Note

If you see an error related with postgre sql wait a minute and just run the command again

Now that the server’s running, visit http://127.0.0.1/

To create a superuser you still can use management commands:

inv docker.manage -e staging -c "createsuperuser"

Installing recorder program

This tutorial is written for Python 2.7 and Ubuntu 12.04 or later.

Installing on Ubuntu

We’ll get started by setting up our environment.

sudo apt-get install python-dev python-pip python-virtualenv git-core alsa-utils vorbis-tools

Next, download the project and create the virual environment:

git clone https://github.com/iago1460/django-radio-recorder.git
cd django-radio-recorder

Create and activate a virtual env:

virtualenv venv
source venv/bin/activate

Install the requirements:

pip install -r requirements.txt

Using your favorite text editor, configure the settings.ini file

Launch the program

python main.py

Communication Setup

In your Admin interface go to Podcast Configuration, copy the Recorder token and put it into the Recorder Program settings:

token:8fdde6d703c05773084ea83e5ec2da62637666a0 #for example

Modify the url in your Recorder Program settings:

url:http://yourdomain:80/api/1/

Customize

Technical reference material.

RadioCo has a number of settings to configure its behaviour, please read this section carefully.

Application Setup

RadioCo can be adapted to your needs, you have three ways to do it. Editing the Global Settings section in the administration page using your browser or manually overriding the settings.py and overriding templates.

Global settings

Go to the admin section on your browser and edit the information available.

Global Configuration

In this section you can add information related to your site apart of the google analytics id.

Calendar Configuration

Settings related to the calendar, things like the first day of the week

Podcast Configuration

Settings related with the recorder, change here recording delays and get the Recorder token necessary for the recorder programme to work

Templates

There is a empty folder called templates inside the radioco folder. You should override templates here, make sure to keep the relative path.

For example, to override the episode detail page copy the episode_detail.html file from radioco/apps/programmes/templates/programmes/episode_detail.html to radioco/templates/programmes/episode_detail.html

Settings.py

These settings are available in settings.py. Your settings should be in a local_settings.py file in the same directory as settings.py.

Warning

Your changes on settings should be in local_settings.py to avoid conflicts when update, create that file if it’s necessary. Be aware that this file is excluded from Git.

USERNAME_RADIOCO_RECORDER

This specifies who is the user of the recorder program:

USERNAME_RADIOCO_RECORDER = 'RadioCo_Recorder'

Note

It’s a good idea change this value for security reasons.

PROGRAMME_LANGUAGES

New in version 1.1

Default: A tuple of the following three languages.

This specifies which languages are available for language selection in your programmes:

gettext_noop = lambda s: s

PROGRAMME_LANGUAGES = (
    ('es', gettext_noop('Spanish')),
    ('en', gettext_noop('English')),
    ('gl', gettext_noop('Galician')),
)

You can see the current list of translated languages by looking in django/conf/global_settings.py (or view the online source).

Disqus

New in version 2.0

Default: Disabled by default.

Add comments to your site with Disqus. Create your account and get your API key.:

DISQUS_ENABLE = True
DISQUS_API_KEY = 'YOUR_API_KEY'
DISQUS_WEBSITE_SHORTNAME = 'YOUR_SHORTNAME'

Recorder Program Setup

The settings are available in your settings.ini.

API

Api documentation.

Browsable API

New in version 3.0

RadioCo has a Web browsable API, go to http://127.0.0.1:8000/api/2/ in your browser to explore it.

Optionally these endpoints support filtering and ordering in the majority of the exposed fields.

Programmes

Programmes can be filter using after and before parameters as well as Transmissions.

Example query to get all available programmes on New Year’s Eve order by name:

http://127.0.0.1:8000/api/2/programmes?after=2016-12-31&before=2016-12-31&ordering=name
Transmissions

Transmissions are always ordered by date, the after and before parameters are required.

Example query:

http://127.0.0.1:8000/api/2/transmissions?after=2016-12-19&before=2016-12-26

Also is possible to request the dates in a specific timezone:

http://127.0.0.1:8000/api/2/transmissions?timezone=Europe%2FMadrid&after=2016-12-19&before=2016-12-26

Finally, there is a endpoint to get the current transmission:

http://127.0.0.1:8000/api/2/transmissions/now

Development & community

RadioCo is an open-source project, and relies on its community of users to keep getting better.

You don’t need to be an expert developer to make a valuable contribution - all you need is a little knowledge of the system, and a willingness to follow the contribution guidelines.

Remember that contributions to the documentation are highly prized, and key to the success of the project. Any time and effort you are willing to contribute is greatly appreciated!

Branch policy

  • master: this is the current stable release, the version released on PyPI.
  • develop: this branch always reflects a state with the latest delivered development changes for the next release.
  • feature branches: these are used to develop new features.

Contributing Translation

For translators we have a Transifex account where you can translate the .po files and don’t need to install git to be able to contribute. All changes there will be automatically sent to the project.

Contributing Documentation

Perhaps considered “boring” by hard-core coders, documentation is sometimes even more important than code! This is what brings fresh blood to a project, and serves as a reference for old timers. On top of this, documentation is the one area where less technical people can help most - you just need to write semi-decent English. People need to understand you. We don’t care about style or correctness.

Documentation should be:

  • written using valid Sphinx/restructuredText syntax (see below for specifics) and the file extension should be .rst
  • written in English (we have standardised on British spellings)
  • accessible - you should assume the reader to be moderately familiar with Python and Django, but not anything else. Link to documentation of libraries you use, for example, even if they are “obvious” to you

Merging documentation is pretty fast and painless.

Except for the tiniest of change, we recommend that you test them before submitting.

Documentation markup
Sections

We use Python documentation conventions for section marking:

  • # with overline, for parts
  • * with overline, for chapters
  • =, for sections
  • -, for subsections
  • ^, for subsubsections
  • ", for paragraphs
Inline markup
  • use backticks - ``settings.py`` - for:
    • literals
    • filenames
    • names of fields and other items in the Admin interface:
  • use emphasis - *Home* around:
    • the names of available options in the Admin
    • values in or of fields
References

Use absolute links to other documentation pages - :doc:`/how_to/toolbar` - rather than relative links - :doc:`/../toolbar`. This makes it easier to run search-and-replaces when items are moved in the structure.

django-radio

manage module

radioco package

Subpackages
radioco.apps package
Subpackages
radioco.apps.api package
Subpackages
radioco.apps.api.tests package
Submodules
radioco.apps.api.tests.test_api module
class radioco.apps.api.tests.test_api.TestSerializers(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

test_episode()[source]
test_episode_programme()[source]
test_programme()[source]
test_programme_photo_url()[source]
test_schedule()[source]
test_transmission()[source]
radioco.apps.api.tests.test_fullcalendar module
class radioco.apps.api.tests.test_fullcalendar.TestFullCalendarApi(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

setUp()[source]
test_move_schedule(*args, **kwargs)[source]
test_move_schedule_in_tz(*args, **kwargs)[source]
test_move_schedule_with_schedules(*args, **kwargs)[source]
test_move_schedule_with_schedules_in_tz(*args, **kwargs)[source]
test_schedules_post(*args, **kwargs)[source]
test_schedules_post_in_tz(*args, **kwargs)[source]
radioco.apps.api.tests.test_programmes module
class radioco.apps.api.tests.test_programmes.TestNotAllowedMethodsProgrammesAPI(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

test_episodes_delete()[source]
test_episodes_post()[source]
test_episodes_put()[source]
test_programmes_delete()[source]
test_programmes_post()[source]
test_programmes_put()[source]
class radioco.apps.api.tests.test_programmes.TestProgrammesAPI(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

setUp()[source]
test_api()[source]
test_episodes_get_all()[source]
test_episodes_get_by_programme()[source]
test_programmes_after()[source]
test_programmes_before()[source]
test_programmes_between()[source]
test_programmes_get_all()[source]
radioco.apps.api.tests.test_recorder module
class radioco.apps.api.tests.test_recorder.TestProgrammesAPI(methodName='runTest')[source]

Bases: rest_framework.test.APITestCase

classmethod setUpTestData()[source]
test_recording_schedules(*args, **keywargs)[source]
test_submit_recorder(*args, **keywargs)[source]
radioco.apps.api.tests.test_schedules module
class radioco.apps.api.tests.test_schedules.TestRestrictedMethodsScheduleAPI(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

test_schedules_delete()[source]
test_schedules_post()[source]
test_schedules_put()[source]
test_transmissions_delete()[source]
test_transmissions_post()[source]
test_transmissions_put()[source]
class radioco.apps.api.tests.test_schedules.TestSchedulesAPI(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

test_schedules_get_all()[source]
test_schedules_get_by_calendar()[source]
test_schedules_get_by_nonexisting_calendar()[source]
test_schedules_get_by_nonexisting_programme()[source]
test_schedules_get_by_nonexiting_type()[source]
test_schedules_get_by_programme()[source]
test_schedules_get_by_type()[source]
class radioco.apps.api.tests.test_schedules.TestTransmissionAPI(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, rest_framework.test.APITestCase

test_incorrect_transmission_queries()[source]
test_transmission_before(*args, **keywargs)[source]
test_transmission_list_filter_non_active_calendar(*args, **keywargs)[source]
test_transmission_now(*args, **keywargs)[source]
test_transmission_same_day()[source]
test_transmissions_between(*args, **keywargs)[source]
test_transmissions_between_requesting_tz()[source]
test_transmissions_filter_calendar_nonexistend()[source]
radioco.apps.api.tests.test_schedules.mock_now()[source]
Module contents
Submodules
radioco.apps.api.radiocom_views module
radioco.apps.api.radiocom_views.programmes_json(request)[source]
radioco.apps.api.radiocom_views.station_json(request)[source]
radioco.apps.api.recorder_views module
radioco.apps.api.recorder_views.check_recorder_program(user)[source]
radioco.apps.api.serializers module
class radioco.apps.api.serializers.DateTimeFieldTz(format=<class rest_framework.fields.empty>, input_formats=None, default_timezone=None, *args, **kwargs)[source]

Bases: rest_framework.fields.DateTimeField

Field to display the datetime in the current timezone

to_representation(date)[source]
class radioco.apps.api.serializers.EpisodeSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]

Bases: rest_framework.serializers.ModelSerializer

class Meta[source]
fields = ('title', 'programme', 'summary', 'issue_date', 'season', 'number_in_season')
model

alias of Episode

class radioco.apps.api.serializers.ProgrammeSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]

Bases: rest_framework.serializers.ModelSerializer

class Meta[source]
fields = ('id', 'slug', 'name', 'synopsis', 'runtime', 'photo', 'language', 'category')
model

alias of Programme

class radioco.apps.api.serializers.ScheduleSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]

Bases: rest_framework.serializers.ModelSerializer

class Meta[source]
fields = ('id', 'programme', 'calendar', 'start', 'runtime', 'title', 'type', 'source')
model

alias of Schedule

ScheduleSerializer.get_title(schedule)[source]
ScheduleSerializer.validate(attrs)[source]
class radioco.apps.api.serializers.TransmissionSerializer(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]

Bases: rest_framework.serializers.Serializer

class radioco.apps.api.serializers.TransmissionSerializerLight(instance=None, data=<class rest_framework.fields.empty>, **kwargs)[source]

Bases: rest_framework.serializers.Serializer

validate(attrs)[source]
radioco.apps.api.urls module
radioco.apps.api.views module
class radioco.apps.api.views.EpisodeFilter(*args, **kwargs)[source]

Bases: rest_framework.filters.FilterSet

class Meta[source]
fields = ('programme',)
model

alias of Episode

EpisodeFilter.base_filters = OrderedDict([(u'programme', <django_filters.filters.CharFilter object at 0x7fa494c19910>)])
EpisodeFilter.declared_filters = OrderedDict([('programme', <django_filters.filters.CharFilter object at 0x7fa494c19910>)])
class radioco.apps.api.views.EpisodeViewSet(**kwargs)[source]

Bases: rest_framework.viewsets.ReadOnlyModelViewSet

filter_backends = (<class 'rest_framework.filters.DjangoFilterBackend'>, <class 'rest_framework.filters.OrderingFilter'>)
filter_class

alias of EpisodeFilter

queryset
serializer_class

alias of EpisodeSerializer

suffix = None
class radioco.apps.api.views.ProgrammeFilter(*args, **kwargs)[source]

Bases: rest_framework.filters.FilterSet

class Meta[source]
fields = ('name', 'category')
model

alias of Programme

ProgrammeFilter.base_filters = OrderedDict([(u'name', <django_filters.filters.CharFilter object at 0x7fa494d63c10>), (u'category', <django_filters.filters.ChoiceFilter object at 0x7fa494d63bd0>)])
ProgrammeFilter.declared_filters = OrderedDict()
class radioco.apps.api.views.ProgrammeFilterForm(data=None, files=None, auto_id=u'id_%s', prefix=None, initial=None, error_class=<class 'django.forms.utils.ErrorList'>, label_suffix=None, empty_permitted=False)[source]

Bases: django.forms.forms.Form

base_fields = OrderedDict([('after', <django.forms.fields.DateField object at 0x7fa494d63150>), ('before', <django.forms.fields.DateField object at 0x7fa494c19750>)])
clean()[source]
declared_fields = OrderedDict([('after', <django.forms.fields.DateField object at 0x7fa494d63150>), ('before', <django.forms.fields.DateField object at 0x7fa494c19750>)])
media
class radioco.apps.api.views.ProgrammeViewSet(**kwargs)[source]

Bases: rest_framework.viewsets.ModelViewSet

filter_backends = (<class 'rest_framework.filters.DjangoFilterBackend'>, <class 'rest_framework.filters.OrderingFilter'>)
filter_class

alias of ProgrammeFilter

list(request, *args, **kwargs)[source]
lookup_field = 'slug'
permission_classes = (<class 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'>,)
queryset
serializer_class

alias of ProgrammeSerializer

suffix = None
class radioco.apps.api.views.ScheduleFilter(*args, **kwargs)[source]

Bases: rest_framework.filters.FilterSet

class Meta[source]
fields = ('programme', 'calendar', 'type')
model

alias of Schedule

ScheduleFilter.base_filters = OrderedDict([(u'programme', <django_filters.filters.CharFilter object at 0x7fa494c19a90>), (u'calendar', <django_filters.filters.ModelChoiceFilter object at 0x7fa494c19b90>), (u'type', <django_filters.filters.ChoiceFilter object at 0x7fa494c19bd0>)])
ScheduleFilter.declared_filters = OrderedDict([('programme', <django_filters.filters.CharFilter object at 0x7fa494c19a90>)])
class radioco.apps.api.views.ScheduleViewSet(**kwargs)[source]

Bases: rest_framework.viewsets.ModelViewSet

filter_backends = (<class 'rest_framework.filters.DjangoFilterBackend'>, <class 'rest_framework.filters.OrderingFilter'>)
filter_class

alias of ScheduleFilter

permission_classes = (<class 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'>,)
queryset
serializer_class

alias of ScheduleSerializer

suffix = None
class radioco.apps.api.views.TransmissionForm(data=None, files=None, auto_id=u'id_%s', prefix=None, initial=None, error_class=<class 'django.forms.utils.ErrorList'>, label_suffix=None, empty_permitted=False)[source]

Bases: django.forms.forms.Form

base_fields = OrderedDict([('after', <django.forms.fields.DateField object at 0x7fa494c19d90>), ('before', <django.forms.fields.DateField object at 0x7fa494c19e10>), ('calendar', <django.forms.fields.CharField object at 0x7fa494c19e90>), ('timezone', <django.forms.fields.ChoiceField object at 0x7fa494c19f10>)])
clean()[source]
clean_timezone()[source]
declared_fields = OrderedDict([('after', <django.forms.fields.DateField object at 0x7fa494c19d90>), ('before', <django.forms.fields.DateField object at 0x7fa494c19e10>), ('calendar', <django.forms.fields.CharField object at 0x7fa494c19e90>), ('timezone', <django.forms.fields.ChoiceField object at 0x7fa494c19f10>)])
media
x = 'Zulu'
class radioco.apps.api.views.TransmissionOperationViewSet(**kwargs)[source]

Bases: radioco.apps.api.viewsets.UpdateOnlyModelViewSet

perform_update(serializer)[source]
permission_classes = (<class 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'>,)
queryset
serializer_class

alias of TransmissionSerializerLight

suffix = None
update(request, *args, **kwargs)[source]
class radioco.apps.api.views.TransmissionViewSet(**kwargs)[source]

Bases: rest_framework.viewsets.ReadOnlyModelViewSet

filter_backends = (<class 'rest_framework.filters.DjangoFilterBackend'>,)
filter_class

alias of ScheduleFilter

list(request, *args, **kwargs)[source]
now(request)[source]
queryset
serializer_class

alias of TransmissionSerializer

suffix = None
radioco.apps.api.viewsets module
class radioco.apps.api.viewsets.UpdateOnlyModelViewSet(**kwargs)[source]

Bases: rest_framework.mixins.UpdateModelMixin, rest_framework.viewsets.GenericViewSet

A viewset that provides a update action.

Module contents
radioco.apps.global_settings package
Subpackages
radioco.apps.global_settings.migrations package
Submodules
radioco.apps.global_settings.migrations.0001_initial module
class radioco.apps.global_settings.migrations.0001_initial.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = []
operations = [<CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'scroll_time', <django.db.models.fields.TimeField>), (u'first_day', <django.db.models.fields.IntegerField>), (u'min_time', <django.db.models.fields.TimeField>), (u'max_time', <django.db.models.fields.TimeField>), (u'display_next_weeks', <django.db.models.fields.PositiveIntegerField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'default_permissions': (u'change',), u'verbose_name': u'Calendar Configuration', u'verbose_name_plural': u'Calendar Configuration'}, name=u'CalendarConfiguration'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'url_source', <django.db.models.fields.CharField>), (u'start_delay', <django.db.models.fields.PositiveIntegerField>), (u'end_delay', <django.db.models.fields.PositiveIntegerField>), (u'next_events', <django.db.models.fields.PositiveIntegerField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'default_permissions': (u'change',), u'verbose_name': u'Podcast Configuration', u'verbose_name_plural': u'Podcast Configuration'}, name=u'PodcastConfiguration'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'site_name', <django.db.models.fields.CharField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'default_permissions': (u'change',), u'verbose_name': u'Global Configuration', u'verbose_name_plural': u'Global Configuration'}, name=u'SiteConfiguration'>]
radioco.apps.global_settings.migrations.0002_remove_calendarconfiguration_display_next_weeks module
class radioco.apps.global_settings.migrations.0002_remove_calendarconfiguration_display_next_weeks.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'global_settings', u'0001_initial')]
operations = [<RemoveField name=u'display_next_weeks', model_name=u'calendarconfiguration'>]
radioco.apps.global_settings.migrations.0004_auto_20150606_1335 module
class radioco.apps.global_settings.migrations.0004_auto_20150606_1335.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'global_settings', u'0003_siteconfiguration_footer')]
operations = [<AddField field=<django.db.models.fields.TextField>, preserve_default=True, name=u'address', model_name=u'siteconfiguration'>, <AddField field=<django.db.models.fields.CharField>, preserve_default=True, name=u'facebook_address', model_name=u'siteconfiguration'>, <AddField field=<django.db.models.fields.CharField>, preserve_default=True, name=u'google_analytics_id', model_name=u'siteconfiguration'>, <AddField field=<django.db.models.fields.CharField>, preserve_default=True, name=u'twitter_address', model_name=u'siteconfiguration'>, <AlterField field=<django.db.models.fields.TextField>, preserve_default=True, name=u'footer', model_name=u'siteconfiguration'>]
radioco.apps.global_settings.migrations.0005_auto_20150606_1415 module
class radioco.apps.global_settings.migrations.0005_auto_20150606_1415.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'global_settings', u'0004_auto_20150606_1335')]
operations = [<RenameField new_name=u'about_footer', model_name=u'siteconfiguration', old_name=u'footer'>, <AddField field=<django.db.models.fields.TextField>, preserve_default=True, name=u'more_about_us', model_name=u'siteconfiguration'>]
radioco.apps.global_settings.migrations.0006_auto_20160116_1509 module
class radioco.apps.global_settings.migrations.0006_auto_20160116_1509.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'global_settings', u'0005_auto_20150606_1415')]
operations = [<AlterField field=<django.db.models.fields.CharField>, name=u'google_analytics_id', model_name=u'siteconfiguration'>]
radioco.apps.global_settings.migrations.0007_dev_calendarconfiguration_tweaks module
radioco.apps.global_settings.migrations.0008_dev_radiocomconfiguration module
Module contents
radioco.apps.global_settings.templatetags package
Submodules
radioco.apps.global_settings.templatetags.global_settings_extras module
radioco.apps.global_settings.templatetags.global_settings_extras.get_global_model(model_path)[source]
Module contents
Submodules
radioco.apps.global_settings.admin module
class radioco.apps.global_settings.admin.PodcastConfigurationAdmin(model, admin_site)[source]

Bases: radioco.apps.global_settings.admin.SingletonModelAdmin

media
readonly_fields = ['recorder_token']
class radioco.apps.global_settings.admin.SingletonModelAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

change_form_template = 'global_settings/admin_change_form.html'
change_view(request, object_id, form_url='', extra_context=None)[source]
get_urls()[source]
has_add_permission(request)[source]
has_delete_permission(request, obj=None)[source]
media
object_history_template = 'global_settings/admin_object_history.html'
response_change(request, obj)[source]
radioco.apps.global_settings.models module
class radioco.apps.global_settings.models.CalendarConfiguration(id, slot_duration, first_day, min_time, max_time)[source]

Bases: radioco.apps.global_settings.models.SingletonModel

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception CalendarConfiguration.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

CalendarConfiguration.get_first_day_display(*moreargs, **morekwargs)
CalendarConfiguration.objects = <radioco.apps.global_settings.models.SingletonModelManager object>
class radioco.apps.global_settings.models.PodcastConfiguration(id, url_source, start_delay, end_delay, next_events)[source]

Bases: radioco.apps.global_settings.models.SingletonModel

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception PodcastConfiguration.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

PodcastConfiguration.objects = <radioco.apps.global_settings.models.SingletonModelManager object>
PodcastConfiguration.recorder_token
class radioco.apps.global_settings.models.RadiocomConfiguration(id, station_name, icon_url, big_icon_url, history, latitude, longitude, news_rss, station_photos, stream_url)[source]

Bases: radioco.apps.global_settings.models.SingletonModel

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception RadiocomConfiguration.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

RadiocomConfiguration.objects = <radioco.apps.global_settings.models.SingletonModelManager object>
class radioco.apps.global_settings.models.SingletonModel(*args, **kwargs)[source]

Bases: django.db.models.base.Model

class Meta[source]
abstract = False
SingletonModel.delete(*args, **kwargs)[source]
classmethod SingletonModel.get_global()[source]
SingletonModel.save(*args, **kwargs)[source]
class radioco.apps.global_settings.models.SingletonModelManager[source]

Bases: django.db.models.manager.Manager

get(*args, **kwargs)[source]
class radioco.apps.global_settings.models.SiteConfiguration(id, site_name, about_footer, more_about_us, google_analytics_id, address, twitter_address, facebook_address)[source]

Bases: radioco.apps.global_settings.models.SingletonModel

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception SiteConfiguration.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

SiteConfiguration.objects = <radioco.apps.global_settings.models.SingletonModelManager object>
Module contents
radioco.apps.programmes package
Subpackages
radioco.apps.programmes.migrations package
Submodules
radioco.apps.programmes.migrations.0001_initial module
class radioco.apps.programmes.migrations.0001_initial.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'auth', u'__first__')]
operations = [<CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'title', <django.db.models.fields.CharField>), (u'summary', <django.db.models.fields.TextField>), (u'issue_date', <django.db.models.fields.DateTimeField>), (u'season', <django.db.models.fields.PositiveIntegerField>), (u'number_in_season', <django.db.models.fields.PositiveIntegerField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'episode', u'verbose_name_plural': u'episodes', u'permissions': ((u'see_all_episodes', u'Can see all episodes'),)}, name=u'Episode'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'role', <django.db.models.fields.CharField>), (u'description', <django.db.models.fields.TextField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'contributor', u'verbose_name_plural': u'contributors', u'permissions': ((u'see_all_participants', u'Can see all participants'),)}, name=u'Participant'>, <CreateModel fields=[(u'episode', <django.db.models.fields.related.OneToOneField>), (u'url', <django.db.models.fields.CharField>), (u'mime_type', <django.db.models.fields.CharField>), (u'length', <django.db.models.fields.PositiveIntegerField>), (u'duration', <django.db.models.fields.PositiveIntegerField>)], bases=(<class 'django.db.models.base.Model'>,), options={}, name=u'Podcast'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'name', <django.db.models.fields.CharField>), (u'start_date', <django.db.models.fields.DateField>), (u'end_date', <django.db.models.fields.DateField>), (u'synopsis', <django.db.models.fields.TextField>), (u'photo', <django.db.models.fields.files.ImageField>), (u'language', <django.db.models.fields.CharField>), (u'current_season', <django.db.models.fields.PositiveIntegerField>), (u'category', <django.db.models.fields.CharField>), (u'slug', <django.db.models.fields.SlugField>), (u'_runtime', <django.db.models.fields.PositiveIntegerField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'programme', u'verbose_name_plural': u'programmes', u'permissions': ((u'see_all_programmes', u'Can see all programmes'),)}, name=u'Programme'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'role', <django.db.models.fields.CharField>), (u'description', <django.db.models.fields.TextField>), (u'date_joined', <django.db.models.fields.DateField>), (u'person', <django.db.models.fields.related.ForeignKey>), (u'programme', <django.db.models.fields.related.ForeignKey>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'role', u'verbose_name_plural': u'roles', u'permissions': ((u'see_all_roles', u'Can see all roles'),)}, name=u'Role'>, <AlterUniqueTogether unique_together=set([(u'person', u'programme', u'role')]), name=u'role'>, <AddField field=<django.db.models.fields.related.ManyToManyField>, preserve_default=True, name=u'announcers', model_name=u'programme'>, <AddField field=<django.db.models.fields.related.ForeignKey>, preserve_default=True, name=u'episode', model_name=u'participant'>, <AddField field=<django.db.models.fields.related.ForeignKey>, preserve_default=True, name=u'person', model_name=u'participant'>, <AlterUniqueTogether unique_together=set([(u'person', u'episode', u'role')]), name=u'participant'>, <AddField field=<django.db.models.fields.related.ManyToManyField>, preserve_default=True, name=u'people', model_name=u'episode'>, <AddField field=<django.db.models.fields.related.ForeignKey>, preserve_default=True, name=u'programme', model_name=u'episode'>, <AlterUniqueTogether unique_together=set([(u'season', u'number_in_season', u'programme')]), name=u'episode'>]
radioco.apps.programmes.migrations.0002_change_language_choices module
class radioco.apps.programmes.migrations.0002_change_language_choices.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0001_initial')]
operations = [<RunPython <function lowercase_language at 0x7fa4936e8f50>>, <AlterField field=<django.db.models.fields.CharField>, name=u'language', model_name=u'programme'>]
radioco.apps.programmes.migrations.0002_change_language_choices.lowercase_language(apps, schema_editor)[source]
radioco.apps.programmes.migrations.0003_change_textfield_to_richtextfield module
class radioco.apps.programmes.migrations.0003_change_textfield_to_richtextfield.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0002_change_language_choices')]
operations = [<AlterField field=<ckeditor.fields.RichTextField>, preserve_default=True, name=u'summary', model_name=u'episode'>, <AlterField field=<django.db.models.fields.CharField>, preserve_default=True, name=u'language', model_name=u'programme'>, <AlterField field=<ckeditor.fields.RichTextField>, preserve_default=True, name=u'synopsis', model_name=u'programme'>]
radioco.apps.programmes.migrations.0004_change_photo_url module
class radioco.apps.programmes.migrations.0004_change_photo_url.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0003_change_textfield_to_richtextfield')]
operations = [<RunPython <function change_photo_url at 0x7fa49352a938>>]
radioco.apps.programmes.migrations.0004_change_photo_url.change_photo_url(apps, schema_editor)[source]
radioco.apps.programmes.migrations.0005_auto_20150531_1734 module
class radioco.apps.programmes.migrations.0005_auto_20150531_1734.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0004_change_photo_url')]
operations = [<AlterField field=<django.db.models.fields.related.OneToOneField>, preserve_default=True, name=u'episode', model_name=u'podcast'>]
radioco.apps.programmes.migrations.0006_auto_20160104_2029 module
class radioco.apps.programmes.migrations.0006_auto_20160104_2029.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0005_auto_20150531_1734')]
operations = [<AlterField field=<django.db.models.fields.files.ImageField>, name=u'photo', model_name=u'programme'>]
radioco.apps.programmes.migrations.0007_change_default_image module
class radioco.apps.programmes.migrations.0007_change_default_image.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0006_auto_20160104_2029'), (u'users', u'0003_auto_20160104_2029')]
operations = [<RunPython <function change_programmes at 0x7fa499398578>>, <RunPython <function change_users at 0x7fa4942c5a28>>]
radioco.apps.programmes.migrations.0007_change_default_image.change_programmes(apps, schema_editor)[source]
radioco.apps.programmes.migrations.0007_change_default_image.change_users(apps, schema_editor)[source]
radioco.apps.programmes.migrations.0008_auto_20160116_1509 module
class radioco.apps.programmes.migrations.0008_auto_20160116_1509.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0007_change_default_image')]
operations = [<AlterField field=<django.db.models.fields.CharField>, name=u'language', model_name=u'programme'>]
radioco.apps.programmes.migrations.0009_dev_auto_20160820_1634 module
Module contents
Submodules
radioco.apps.programmes.admin module
class radioco.apps.programmes.admin.NonStaffEpisodeAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

fields = ['programme', 'title', 'summary', 'issue_date', 'season', 'number_in_season']
form

alias of NonStaffEpisodeAdminForm

formfield_for_foreignkey(db_field, request=None, **kwargs)[source]
get_queryset(request)[source]
get_readonly_fields(request, obj=None)[source]
inlines = [<class 'radioco.apps.programmes.admin.NonStaffParticipantInline'>, <class 'radioco.apps.programmes.admin.PodcastInline'>]
list_display = ('__unicode__', 'season', 'number_in_season', 'issue_date', 'programme')
list_filter = ['issue_date', <class 'radioco.apps.programmes.admin.OwnEpisodeProgrammeListFilter'>, <class 'radioco.apps.programmes.admin.OwnEpisodeIssueDateListFilter'>]
media
ordering = ['-season', '-number_in_season']
save_formset(request, form, formset, change)[source]
save_model(request, obj, form, change)[source]
search_fields = ['programme__name']
class radioco.apps.programmes.admin.NonStaffEpisodeAdminForm(*args, **kwargs)[source]

Bases: django.forms.models.ModelForm

class Meta[source]
fields = '__all__'
model

alias of Episode

NonStaffEpisodeAdminForm.base_fields = OrderedDict([('title', <django.forms.fields.CharField object at 0x7fa49aa77650>), ('people', <django.forms.models.ModelMultipleChoiceField object at 0x7fa49aa77790>), ('programme', <django.forms.models.ModelChoiceField object at 0x7fa49aa77a10>), ('summary', <ckeditor_uploader.fields.RichTextUploadingFormField object at 0x7fa49aa77c50>), ('issue_date', <django.forms.fields.DateTimeField object at 0x7fa49aa77d50>), ('season', <django.forms.fields.IntegerField object at 0x7fa49aa77e50>), ('number_in_season', <django.forms.fields.IntegerField object at 0x7fa49aa77f90>)])
NonStaffEpisodeAdminForm.clean_programme()[source]
NonStaffEpisodeAdminForm.declared_fields = OrderedDict()
NonStaffEpisodeAdminForm.media
class radioco.apps.programmes.admin.NonStaffParticipantInline(parent_model, admin_site)[source]

Bases: django.contrib.admin.options.StackedInline

extra = 0
form

alias of NonStaffParticipantInlineForm

formset

alias of NonStaffParticipantInlineFormset

get_formset(request, obj=None, **kwargs)[source]
get_queryset(request)[source]
media
model

alias of Participant

class radioco.apps.programmes.admin.NonStaffParticipantInlineForm(*args, **kwargs)[source]

Bases: django.forms.models.ModelForm

base_fields = OrderedDict()
clean()[source]
declared_fields = OrderedDict()
media
class radioco.apps.programmes.admin.NonStaffParticipantInlineFormset(data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs)[source]

Bases: django.forms.models.BaseInlineFormSet

class radioco.apps.programmes.admin.NonStaffProgrammeAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

fieldsets = ((None, {'fields': ('name', 'slug', 'synopsis', 'category', 'current_season', 'photo', 'language', '_runtime')}), (<django.utils.functional.__proxy__ object at 0x7fa49aa770d0>, {'fields': ('start_date', 'end_date')}))
get_form(request, obj=None, **kwargs)[source]
get_queryset(request)[source]
inlines = [<class 'radioco.apps.programmes.admin.NonStaffRoleInline'>]
list_display = ('name', '_runtime', 'current_season', 'category', 'start_date', 'end_date')
list_filter = ['_runtime', 'category']
media
save_formset(request, form, formset, change)[source]
search_fields = ['name']
class radioco.apps.programmes.admin.NonStaffRoleInline(parent_model, admin_site)[source]

Bases: django.contrib.admin.options.StackedInline

extra = 0
form

alias of NonStaffRoleInlineForm

formset

alias of NonStaffRoleInlineFormset

get_formset(request, obj=None, **kwargs)[source]
get_queryset(request)[source]
media
model

alias of Role

class radioco.apps.programmes.admin.NonStaffRoleInlineForm(*args, **kwargs)[source]

Bases: django.forms.models.ModelForm

base_fields = OrderedDict()
clean()[source]

Check unique together: person, role, programme

declared_fields = OrderedDict()
media
class radioco.apps.programmes.admin.NonStaffRoleInlineFormset(data=None, files=None, instance=None, save_as_new=False, prefix=None, queryset=None, **kwargs)[source]

Bases: django.forms.models.BaseInlineFormSet

class radioco.apps.programmes.admin.OwnEpisodeIssueDateListFilter(request, params, model, model_admin)[source]

Bases: django.contrib.admin.filters.SimpleListFilter

lookups(request, model_admin)[source]
parameter_name = 'date'
queryset(request, queryset)[source]
title = <django.utils.functional.__proxy__ object>
class radioco.apps.programmes.admin.OwnEpisodeProgrammeListFilter(request, params, model, model_admin)[source]

Bases: django.contrib.admin.filters.SimpleListFilter

Check people in programmes besides episodes, better performance

lookups(request, model_admin)[source]
parameter_name = 'programme'
queryset(request, queryset)[source]
title = <django.utils.functional.__proxy__ object>
class radioco.apps.programmes.admin.PodcastInline(parent_model, admin_site)[source]

Bases: django.contrib.admin.options.StackedInline

extra = 0
inline_classes = ('grp-collapse grp-open',)
media
model

alias of Podcast

radioco.apps.programmes.feeds module
class radioco.apps.programmes.feeds.ProgrammeFeed[source]

Bases: django.contrib.syndication.views.Feed

description(programme)[source]
feed_extra_kwargs(programme)[source]
get_object(request, slug)[source]
item_description(podcast)[source]
item_enclosure_length(podcast)[source]
item_enclosure_mime_type(podcast)[source]
item_enclosure_url(podcast)[source]
item_extra_kwargs(podcast)[source]
item_keywords(podcast)[source]
item_pubdate(podcast)[source]
item_title(podcast)[source]
items(programme)[source]
title(programme)[source]
class radioco.apps.programmes.feeds.RssProgrammeFeed[source]

Bases: radioco.apps.programmes.feeds.ProgrammeFeed

description(programme)[source]
feed_type

alias of iTunesFeed

item_guid(episode)[source]
class radioco.apps.programmes.feeds.iTunesFeed(title, link, description, language=None, author_email=None, author_name=None, author_link=None, subtitle=None, categories=None, feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs)[source]

Bases: django.utils.feedgenerator.Rss201rev2Feed

add_item_elements(handler, item)[source]
add_root_elements(handler)[source]
rss_attributes()[source]
radioco.apps.programmes.models module
class radioco.apps.programmes.models.Episode(id, title, programme, summary, issue_date, season, number_in_season)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Episode.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Episode.get_absolute_url()[source]
Episode.objects = <radioco.apps.programmes.models.EpisodeManager object>
Episode.participant_set
Episode.people
Episode.podcast
Episode.programme
Episode.runtime
class radioco.apps.programmes.models.EpisodeManager[source]

Bases: django.db.models.manager.Manager

create_episode(date, programme, last_episode=None, episode=None)[source]
static last(programme)[source]
static unfinished(programme, after=None)[source]
class radioco.apps.programmes.models.Participant(id, person, episode, role, description)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Participant.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Participant.episode
Participant.get_role_display(*moreargs, **morekwargs)
Participant.objects = <django.db.models.manager.Manager object>
Participant.person
class radioco.apps.programmes.models.Podcast(episode, url, mime_type, length, duration)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Podcast.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Podcast.episode
Podcast.get_absolute_url()[source]
Podcast.objects = <django.db.models.manager.Manager object>
class radioco.apps.programmes.models.Programme(id, name, synopsis, photo, language, current_season, category, slug, _runtime, start_date, end_date)[source]

Bases: django.db.models.base.Model

CATEGORY_CHOICES = (('Arts', <django.utils.functional.__proxy__ object at 0x7fa49ab4e410>), ('Business', <django.utils.functional.__proxy__ object at 0x7fa49ab4e450>), ('Comedy', <django.utils.functional.__proxy__ object at 0x7fa49ab4e4d0>), ('Education', <django.utils.functional.__proxy__ object at 0x7fa49ab4e550>), ('Games & Hobbies', <django.utils.functional.__proxy__ object at 0x7fa49ab4e5d0>), ('Government & Organizations', <django.utils.functional.__proxy__ object at 0x7fa49ab4e650>), ('Health', <django.utils.functional.__proxy__ object at 0x7fa49ab4e6d0>), ('Kids & Family', <django.utils.functional.__proxy__ object at 0x7fa49ab4e750>), ('Music', <django.utils.functional.__proxy__ object at 0x7fa49ab4e7d0>), ('News & Politics', <django.utils.functional.__proxy__ object at 0x7fa49ab4e850>), ('Religion & Spirituality', <django.utils.functional.__proxy__ object at 0x7fa49ab4e8d0>), ('Science & Medicine', <django.utils.functional.__proxy__ object at 0x7fa49ab4e950>), ('Society & Culture', <django.utils.functional.__proxy__ object at 0x7fa49ab4e9d0>), ('Sports & Recreation', <django.utils.functional.__proxy__ object at 0x7fa49ab4ea50>), ('Technology', <django.utils.functional.__proxy__ object at 0x7fa49ab4ead0>), ('TV & Film', <django.utils.functional.__proxy__ object at 0x7fa49ab4eb50>))
exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Programme.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Programme.announcers
Programme.end_dt
Programme.episode_set
Programme.get_absolute_url()[source]
Programme.get_category_display(*moreargs, **morekwargs)
Programme.get_language_display(*moreargs, **morekwargs)
Programme.objects = <django.db.models.manager.Manager object>
Programme.rearrange_episodes(after, calendar)[source]

Update the issue_date of episodes from a given date

Programme.role_set
Programme.runtime
Programme.save(*args, **kwargs)[source]
Programme.schedule_set
Programme.start_dt
class radioco.apps.programmes.models.Role(id, person, programme, role, description, date_joined)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Role.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Role.get_next_by_date_joined(*moreargs, **morekwargs)
Role.get_previous_by_date_joined(*moreargs, **morekwargs)
Role.get_role_display(*moreargs, **morekwargs)
Role.objects = <django.db.models.manager.Manager object>
Role.person
Role.programme
radioco.apps.programmes.models.update_schedule_if_dt_has_changed(sender, instance, **kwargs)[source]
radioco.apps.programmes.models.update_schedule_performance(programme)[source]
radioco.apps.programmes.tests module
class radioco.apps.programmes.tests.EpisodeManagerTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_create_episode()[source]
test_issue_date()[source]
test_last()[source]
test_last_none()[source]
test_number_in_season()[source]
test_people()[source]
test_programme()[source]
test_season()[source]
test_unfinished()[source]
test_unfinished_none()[source]
class radioco.apps.programmes.tests.EpisodeModelTests(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

setUp()[source]
test_absoulte_url()[source]
test_model_manager()[source]
test_runtime()[source]
test_str()[source]
class radioco.apps.programmes.tests.ProgrammeModelAdminTests(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

setUp()[source]
test_fieldset()[source]
class radioco.apps.programmes.tests.ProgrammeModelTests(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

setUp()[source]
test_absolute_url()[source]
test_runtime()[source]
test_runtime_is_zero()[source]
test_runtime_not_get()[source]
test_runtime_not_set()[source]
test_save_programme()[source]
test_slug()[source]
test_str()[source]
radioco.apps.programmes.urls module
radioco.apps.programmes.utils module
radioco.apps.programmes.views module
radioco.apps.programmes.views.episode_detail(request, slug, season_number, episode_number)[source]
radioco.apps.programmes.views.programme_detail(request, slug)[source]
Module contents
radioco.apps.radioco package
Subpackages
radioco.apps.radioco.management package
Subpackages
radioco.apps.radioco.management.commands package
Submodules
radioco.apps.radioco.management.commands.create_example_data module
class radioco.apps.radioco.management.commands.create_example_data.Command(stdout=None, stderr=None, no_color=False)[source]

Bases: django.core.management.base.BaseCommand

handle(*args, **options)[source]
Module contents
Module contents
radioco.apps.radioco.tests package
Submodules
radioco.apps.radioco.tests.tests module
class radioco.apps.radioco.tests.tests.RadioIntegrationTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

test_index()[source]
class radioco.apps.radioco.tests.tests.UtilsTest(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

test_dictionary_key()[source]

Making sure that is safe to use a dt tz aware object in a different timezone to access a dictionary

test_example_data()[source]

Running example data function, If nothing crash we are happy

Module contents
Submodules
radioco.apps.radioco.context_processors module
radioco.apps.radioco.context_processors.settings(request)[source]
radioco.apps.radioco.test_utils module
class radioco.apps.radioco.test_utils.TestDataMixin[source]

Bases: object

classmethod setUpTestData()[source]
radioco.apps.radioco.test_utils.create_test_data()[source]
radioco.apps.radioco.test_utils.spain_tz()[source]
radioco.apps.radioco.tz_utils module
class radioco.apps.radioco.tz_utils.GMT(seconds)[source]

Bases: dateutil.tz.tz.tzoffset

GMT implementation, it has a fixed offset

localize(dt, is_dst=False)[source]

Convert naive time to local time

normalize(dt, is_dst=False)[source]

Correct the timezone information on the given datetime

radioco.apps.radioco.tz_utils.fix_recurrence_date(start_dt, dt)[source]

Fix for django-recurrence 1.3 rdates and exdates needs a datetime, we are combining the date with the time from start_date.

Return: A datetime in the default timezone with the offset required to work in the recurrence

radioco.apps.radioco.tz_utils.fix_recurrence_dst(dt)[source]

Fix for django-recurrence 1.3 Function to fix a datetime tz aware with an incorrect offset

Returns: A datetime in the same timezone but with the offset fixed

radioco.apps.radioco.tz_utils.get_active_timezone()[source]

Same method as timezone.get_current_timezone but returning utc if nothing was set

radioco.apps.radioco.tz_utils.recurrence_after(recurrence, after_dt, start_dt)[source]

Fix for django-recurrence 1.3 Avoid outputting a impossible dt

radioco.apps.radioco.tz_utils.recurrence_before(recurrence, before_dt, start_dt)[source]

Fix for django-recurrence 1.3 Avoid outputting a non impossible dt

radioco.apps.radioco.tz_utils.transform_datetime_tz(dt, tz=None)[source]

Transform a datetime in other timezone to the current one

radioco.apps.radioco.tz_utils.transform_dt_to_default_tz(dt)[source]

Transform a datetime in other timezone to the current one

radioco.apps.radioco.utils module
class radioco.apps.radioco.utils.DeletePermissionMixin[source]

Bases: object

dispatch(request, *args, **kwargs)[source]
model = None
class radioco.apps.radioco.utils.GetObjectMixin[source]

Bases: django.views.generic.detail.SingleObjectMixin

dispatch(*args, **kwargs)[source]
object = None
radioco.apps.radioco.utils.check_delete_permission(user, model)[source]
radioco.apps.radioco.utils.create_example_data()[source]
radioco.apps.radioco.utils.field_has_changed(_object, field)[source]
class radioco.apps.radioco.utils.memorize(func)[source]

Bases: dict

A simple cache system, use as decorator

radioco.apps.radioco.views module
radioco.apps.radioco.views.index(request)[source]
radioco.apps.radioco.views.user_logout(request)[source]
Module contents
radioco.apps.schedules package
Subpackages
radioco.apps.schedules.migrations package
Submodules
radioco.apps.schedules.migrations.0001_initial module
class radioco.apps.schedules.migrations.0001_initial.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'programmes', u'0001_initial')]
operations = [<CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'day', <django.db.models.fields.IntegerField>), (u'start_hour', <django.db.models.fields.TimeField>), (u'type', <django.db.models.fields.CharField>), (u'programme', <django.db.models.fields.related.ForeignKey>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'schedule', u'verbose_name_plural': u'schedules'}, name=u'Schedule'>, <CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'name', <django.db.models.fields.CharField>), (u'start_date', <django.db.models.fields.DateField>), (u'end_date', <django.db.models.fields.DateField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'verbose_name': u'schedule board', u'verbose_name_plural': u'schedule board'}, name=u'ScheduleBoard'>, <AddField field=<django.db.models.fields.related.ForeignKey>, preserve_default=True, name=u'schedule_board', model_name=u'schedule'>, <AddField field=<django.db.models.fields.related.ForeignKey>, preserve_default=True, name=u'source', model_name=u'schedule'>]
radioco.apps.schedules.migrations.0002_dev_schedules module
Module contents
radioco.apps.schedules.tests package
Submodules
radioco.apps.schedules.tests.test_recurrences module
class radioco.apps.schedules.tests.test_recurrences.EmptyRecurrenceTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

Tests to check django-recurrence behaviour when there are no recurrences

setUp()[source]
test_after()[source]
test_after_inclusive()[source]
test_before()[source]
test_before_inclusive()[source]
test_no_values()[source]
class radioco.apps.schedules.tests.test_recurrences.RecurrenceTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

Tests to check django-recurrence behaviour

setUp()[source]
test_after()[source]
test_after_inclusive()[source]
test_before_dt()[source]
test_before_inclusive()[source]
test_impossible_recurrence_after()[source]

Testing error calling after and function wrapper to solve it (recurrence_after)

test_impossible_recurrence_before()[source]

Testing error calling before and function wrapper to solve it (recurrence_before)

test_last_date_is_equals_before_limit()[source]
test_start_date_is_before_start_limit()[source]
test_when_date_doesnt_exist_on_february()[source]
radioco.apps.schedules.tests.test_schedules module
class radioco.apps.schedules.tests.test_schedules.CalendarAdminTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_clone_calendar()[source]
class radioco.apps.schedules.tests.test_schedules.CalendarManagerTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_current()[source]
test_current_no_date()[source]
class radioco.apps.schedules.tests.test_schedules.CalendarModelTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

test_str()[source]
class radioco.apps.schedules.tests.test_schedules.CalendarValidationTests(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

setUp()[source]
test_only_one_calendar_active()[source]
class radioco.apps.schedules.tests.test_schedules.ScheduleBetweenTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_between_includes_started_episode()[source]
test_dates_between_includes_started_episode()[source]
class radioco.apps.schedules.tests.test_schedules.ScheduleModelTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_date_after()[source]
test_date_after_exclude()[source]
test_date_before()[source]
test_dates_between()[source]
test_dates_between_complex_ruleset()[source]
test_dates_between_earlier_end_by_programme()[source]
test_dates_between_later_start_by_programme()[source]
test_end_gt_calendar()[source]
test_recurrence_rules()[source]
test_runtime()[source]
test_runtime_not_set()[source]
test_save_rearrange_episodes(*args, **keywargs)[source]
test_start()[source]
test_start_lt_calendar()[source]
test_unicode()[source]
class radioco.apps.schedules.tests.test_schedules.ScheduleUtilsTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_available_dates_after()[source]
test_available_dates_none()[source]
test_rearrange_episodes()[source]
test_rearrange_episodes_new_schedule(*args, **keywargs)[source]
test_rearrange_only_non_emited_episodes(*args, **keywargs)[source]
class radioco.apps.schedules.tests.test_schedules.ScheduleValidationTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

test_fields()[source]
class radioco.apps.schedules.tests.test_schedules.TransmissionModelTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_at()[source]
test_between()[source]
test_between_by_queryset()[source]
test_end()[source]
test_episode__url()[source]
test_name()[source]
test_programme_url()[source]
test_slug()[source]
test_start()[source]
radioco.apps.schedules.tests.test_schedules.mock_now(dt=datetime.datetime(2014, 1, 1, 13, 30, tzinfo=<UTC>))[source]
radioco.apps.schedules.tests.test_timezone module
class radioco.apps.schedules.tests.test_timezone.ScheduleModelTests(methodName='runTest')[source]

Bases: radioco.apps.radioco.test_utils.TestDataMixin, django.test.testcases.TestCase

setUp()[source]
test_CEST_transition()[source]
test_CET_transition()[source]
test_cleaned_internal_recurrence_dates()[source]
test_transform_dt_to_default_tz()[source]
radioco.apps.schedules.tests.test_timezone.test_CET_transitions(self)[source]
Module contents
Submodules
radioco.apps.schedules.admin module
class radioco.apps.schedules.admin.CalendarAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

actions = ['clone_calendar', 'set_active']
clone_calendar(request, queryset)[source]
list_display = ('name', 'is_active')
list_filter = ['is_active']
media
ordering = ['name']
search_fields = ['name']
set_active(request, queryset)[source]
class radioco.apps.schedules.admin.ScheduleAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

change_list_template = 'admin/schedules/calendar.html'
changelist_view(request, extra_context=None)[source]
fieldsets = ((None, {'fields': ('programme', 'type', 'start_dt', 'recurrences')}), (<django.utils.functional.__proxy__ object at 0x7fa49aa01450>, {'fields': ('effective_start_dt', 'effective_end_dt', 'from_collection', 'source'), 'classes': ('collapse',)}))
has_add_permission(request)[source]
media
readonly_fields = ('effective_start_dt', 'effective_end_dt', 'source', 'from_collection')
radioco.apps.schedules.forms module
class radioco.apps.schedules.forms.DeleteScheduleForm(has_recurrences, *args, **kwargs)[source]

Bases: django.forms.forms.Form

DELETE_ALL = 'all'
DELETE_ONLY_THIS = 'only_this'
DELETE_THIS_AND_FOLLOWING = 'this_and_following'
base_fields = OrderedDict([('transmission_dt', <django.forms.fields.DateTimeField object at 0x7fa493241890>), ('schedule', <django.forms.models.ModelChoiceField object at 0x7fa4932419d0>)])
declared_fields = OrderedDict([('transmission_dt', <django.forms.fields.DateTimeField object at 0x7fa493241890>), ('schedule', <django.forms.models.ModelChoiceField object at 0x7fa4932419d0>)])
media
radioco.apps.schedules.models module
class radioco.apps.schedules.models.Calendar(id, name, is_active)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Calendar.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

classmethod Calendar.get_active()[source]
Calendar.objects = <django.db.models.manager.Manager object>
Calendar.rearrange_episodes()[source]
Calendar.save(*args, **kwargs)[source]
Calendar.schedule_set
class radioco.apps.schedules.models.CalendarManager[source]

Bases: django.db.models.manager.Manager

current()[source]
class radioco.apps.schedules.models.ExcludedDates(*args, **kwargs)[source]

Bases: django.db.models.base.Model

Helper to improve performance

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception ExcludedDates.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

ExcludedDates.date
ExcludedDates.get_new_excluded_datetime(new_dt)[source]

Returns: A new dt to be excluded in that date

ExcludedDates.get_next_by_datetime(*moreargs, **morekwargs)
ExcludedDates.get_previous_by_datetime(*moreargs, **morekwargs)
ExcludedDates.objects = <django.db.models.manager.Manager object>
ExcludedDates.schedule
class radioco.apps.schedules.models.Schedule(id, programme, type, calendar, recurrences, start_dt, effective_start_dt, effective_end_dt, from_collection, source)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception Schedule.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

Schedule.calendar
Schedule.child_schedules
Schedule.date_after(after)[source]
Schedule.date_before(before)[source]
Schedule.dates_between(after, before)[source]

Return a sorted list of dates between after and before

Schedule.exclude_date(dt)[source]
Schedule.excludeddates_set
Schedule.from_collection
Schedule.get_next_by_start_dt(*moreargs, **morekwargs)
Schedule.get_previous_by_start_dt(*moreargs, **morekwargs)
static Schedule.get_schedule_which_excluded_dt(programme, dt)[source]
Schedule.get_type_display(*moreargs, **morekwargs)
Schedule.has_recurrences()[source]
Schedule.include_date(dt)[source]
Schedule.objects = <django.db.models.manager.Manager object>
Schedule.programme
Schedule.recurrences

A placeholder class that provides a way to set the attribute on the model.

Schedule.runtime
Schedule.save(*args, **kwargs)[source]
Schedule.schedule_set
Schedule.source
class radioco.apps.schedules.models.Transmission(schedule, date, episode=None)[source]

Bases: object

Temporal object generated according to recurrence rules or schedule information It contains concrete dates

classmethod at(at)[source]
classmethod between(after, before, schedules=None)[source]

Return a tuple of Schedule and Transmissions sorted by date

end
episode_url
name
programme
programme_url
slug
radioco.apps.schedules.models.calculate_effective_schedule_end_dt(schedule)[source]

Calculation of the last end date to improve performance

radioco.apps.schedules.models.calculate_effective_schedule_start_dt(schedule)[source]

Calculation of the first start date to improve performance

radioco.apps.schedules.urls module
radioco.apps.schedules.utils module
radioco.apps.schedules.utils.next_dates(calendar, programme, after)[source]

Returns: A generator with the next dates of a given programme

radioco.apps.schedules.views module
class radioco.apps.schedules.views.DeleteScheduleView(**kwargs)[source]

Bases: radioco.apps.radioco.utils.GetObjectMixin, radioco.apps.radioco.utils.DeletePermissionMixin, django.views.generic.edit.FormView

form_class

alias of DeleteScheduleForm

form_valid(form)[source]
get(request, *args, **kwargs)[source]
get_context_data(**kwargs)[source]
get_form_kwargs()[source]
get_initial()[source]
model

alias of Schedule

schedule_id = None
template_name = 'admin/schedules/delete_modal.html'
transmission_dt = None
radioco.apps.schedules.views.schedule_list(request)[source]
Module contents
radioco.apps.users package
Subpackages
radioco.apps.users.migrations package
Submodules
radioco.apps.users.migrations.0001_initial module
class radioco.apps.users.migrations.0001_initial.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'auth', u'__first__')]
operations = [<CreateModel fields=[(u'id', <django.db.models.fields.AutoField>), (u'bio', <django.db.models.fields.TextField>), (u'avatar', <django.db.models.fields.files.ImageField>), (u'display_personal_page', <django.db.models.fields.BooleanField>), (u'slug', <django.db.models.fields.SlugField>), (u'user', <django.db.models.fields.related.OneToOneField>)], bases=(<class 'django.db.models.base.Model'>,), options={u'default_permissions': (u'change',), u'verbose_name': u'user profile', u'verbose_name_plural': u'user profile'}, name=u'UserProfile'>]
radioco.apps.users.migrations.0002_change_textfield_to_richtextfield module
class radioco.apps.users.migrations.0002_change_textfield_to_richtextfield.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'users', u'0001_initial')]
operations = [<AlterField field=<ckeditor.fields.RichTextField>, preserve_default=True, name=u'bio', model_name=u'userprofile'>]
radioco.apps.users.migrations.0003_auto_20160104_2029 module
class radioco.apps.users.migrations.0003_auto_20160104_2029.Migration(name, app_label)[source]

Bases: django.db.migrations.migration.Migration

dependencies = [(u'users', u'0002_change_textfield_to_richtextfield')]
operations = [<AlterField field=<django.db.models.fields.files.ImageField>, name=u'avatar', model_name=u'userprofile'>]
radioco.apps.users.migrations.0004_dev_ckeditor_upload_field module
Module contents
Submodules
radioco.apps.users.admin module
class radioco.apps.users.admin.NonStaffUserProfileForm(*args, **kwargs)[source]

Bases: django.forms.models.ModelForm

base_fields = OrderedDict([('username', <django.forms.fields.CharField object at 0x7fa49aa5a9d0>), ('first_name', <django.forms.fields.CharField object at 0x7fa49aa5ac50>), ('last_name', <django.forms.fields.CharField object at 0x7fa49aa5add0>), ('email', <django.forms.fields.EmailField object at 0x7fa49aa5af50>)])
clean_username()[source]
declared_fields = OrderedDict([('username', <django.forms.fields.CharField object at 0x7fa49aa5a9d0>), ('first_name', <django.forms.fields.CharField object at 0x7fa49aa5ac50>), ('last_name', <django.forms.fields.CharField object at 0x7fa49aa5add0>), ('email', <django.forms.fields.EmailField object at 0x7fa49aa5af50>)])
media
save(force_insert=False, force_update=False, commit=True)[source]
class radioco.apps.users.admin.SingletonProfileAdmin(model, admin_site)[source]

Bases: django.contrib.admin.options.ModelAdmin

change_view(request, object_id, extra_context=None)[source]
fields = ['username', 'first_name', 'last_name', 'email', 'bio', 'avatar', 'display_personal_page']
form

alias of NonStaffUserProfileForm

get_urls()[source]
has_add_permission(request)[source]
has_delete_permission(request, obj=None)[source]
media
response_change(request, obj)[source]
save_model(request, obj, form, change)[source]
class radioco.apps.users.admin.UserProfileAdmin(model, admin_site)[source]

Bases: django.contrib.auth.admin.UserAdmin

inlines = (<class 'radioco.apps.users.admin.UserProfileInline'>,)
list_filter = ('is_staff', 'is_superuser', 'is_active', 'groups', 'userprofile__display_personal_page')
media
class radioco.apps.users.admin.UserProfileInline(parent_model, admin_site)[source]

Bases: django.contrib.admin.options.StackedInline

can_delete = False
exclude = ('slug',)
extra = 1
inline_classes = ('grp-collapse grp-open',)
media
model

alias of UserProfile

radioco.apps.users.models module
class radioco.apps.users.models.UserProfile(id, user, bio, avatar, display_personal_page, slug)[source]

Bases: django.db.models.base.Model

exception DoesNotExist

Bases: django.core.exceptions.ObjectDoesNotExist

exception UserProfile.MultipleObjectsReturned

Bases: django.core.exceptions.MultipleObjectsReturned

UserProfile.get_absolute_url()[source]
UserProfile.objects = <django.db.models.manager.Manager object>
UserProfile.save(*args, **kwargs)[source]
UserProfile.user
radioco.apps.users.models.save_slug(sender, instance=None, **kwargs)[source]
radioco.apps.users.tests module
class radioco.apps.users.tests.UserProfileMethodTests(methodName='runTest')[source]

Bases: django.test.testcases.TestCase

test_get_announcers_and_profile()[source]
test_save()[source]
radioco.apps.users.urls module
radioco.apps.users.views module
class radioco.apps.users.views.UsersView(**kwargs)[source]

Bases: django.views.generic.list.ListView

get_queryset()[source]
model

alias of UserProfile

radioco.apps.users.views.userprofile_detail(request, slug)[source]
Module contents
Module contents
radioco.configs package
Subpackages
radioco.configs.base package
Submodules
radioco.configs.base.manage module
radioco.configs.base.settings module
radioco.configs.base.settings.gettext_noop(s)
radioco.configs.base.urls module
Module contents
radioco.configs.development package
Submodules
radioco.configs.development.manage module
radioco.configs.development.settings module
radioco.configs.development.settings.show_toolbar(request)[source]
Module contents
radioco.configs.heroku package
Submodules
radioco.configs.heroku.manage module
radioco.configs.heroku.settings module
radioco.configs.heroku.wsgi module
Module contents
Module contents
Module contents

tasks package

Submodules
tasks.docker module
tasks.docs module
tasks.heroku module
tasks.locale module
tasks.radioco module
tasks.utils module
Module contents

Release notes & upgrade information

Some versions of RadioCo present more complex upgrade paths than others, and some require you to take action. It is strongly recommended to read the release notes carefully when upgrading.

It goes without saying that you should backup your database before embarking on any process that makes changes to your database.

1.1 release notes

What’s new in 1.1
  • Numerous updates to the documentation
  • Updates to facilitate the starting-up (radioco_recorder_user and a empty shedule_board are self-created)
  • Added custom languages to programmes (by default, all django languages)
How this affects you

If you’re starting with a new installation, you don’t need to worry about this. Don’t even bother reading this section; it’s for upgraders.

Activate your virtualenv and do the following in your project main directory:

pip install django-radio==1.1
python manage.py migrate

1.1.1 release notes

What’s new in 1.1.1
  • Added CKEditor to programme’s synopsis, episode’s summary and user’s biography
  • Added default settings to the project
  • Added customisable footer
How this affects you

If you’re starting with a new installation, you don’t need to worry about this. Don’t even bother reading this section; it’s for upgraders.

Activate your virtualenv and do the following in your project main directory:

pip install django-radio==1.1.1
python manage.py collectstatic
python manage.py migrate

In your settings.py import default settings:

from radio.settings_base import *

1.2 release notes

What’s new in 1.2
  • Fixed project urls when RadioCo is deployed in a subdirectory
  • Fixed default url of photos when STATIC_URL is not “/static/”
  • Fixed errors 404 when users don’t have a personal webpage.
How this affects you

If you’re starting with a new installation, you don’t need to worry about this. Don’t even bother reading this section; it’s for upgraders.

Activate your virtualenv and do the following in your project main directory:

pip install django-radio==1.2
python manage.py migrate

2.1 release notes

What’s new in 2.1
  • New layout! The whole site has been redesigned
  • Added image versions, RadioCo will adapt your images to fit in the new template
  • Added comments support.
  • Added analytics support.
How this affects you

If you’re starting with a new installation, you don’t need to worry about this. Don’t even bother reading this section; it’s for upgraders.

You need to replace your current source with the content of https://github.com/iago1460/django-radio. To setup your settings please read the configuration section.

You should be able to keep your current database but make sure to create a backup before start.

pip install -r radio/configs/common/requirements.txt
python manage.py collectstatic
python manage.py migrate

3.0 release notes

What’s new in 3.0

In this version RadioCo has almost been rewritten completely, internally the major improvements are:

  • Use of time-zone-aware datetime objects (more info)
  • Added good amount of tests
  • Changed calendar to use API methods
  • Using bower to download js and css dependencies
  • Renamed internal folders and models
    • django-radio/radio –> django-radio/radioco
    • django-radio/radio/configs/common –> django-radio/radioco/configs/base
    • ScheduleBoard model has been renamed to Calendar
    • dashboard app has been merged into schedules

Special mention to @stefan-walluhn - Radio Corax who helped in this release, specifically adding Bower, Rest Framework API, tests and complex recurrence rules.

And the visible features are:

  • Added download button to episodes (#38 Credit to @AlexCortinas)
  • Removed unnecessary Scroll configuration setting (#13 Credit to @AlexCortinas)
  • Improvements in title representation of episodes when not set (#17 Credit to @txenoo)
  • Removed warnings when compiling (#32 Thanks to @txenoo)
  • Added Radiocom endpoints (Thanks to @pablogrela)
  • Added upload feature to CKEditor (#22)
  • Added new calendar setting slotDuration
  • Added a suite of helper tasks using invoke (Easy deploy to docker)
  • Added public API to access to programmes, episodes, schedules and transmissions (#2)
  • The schedule manager has received important updates (#20, #4)

Now managing schedules should be a lot more simple. From the manager is possible to create and edit programmes in addition to schedules.

The forced weekly recurrence has been removed in favor of complex recurrence rules, having a unique calendar should be enough.

The Calendar model doesn’t have date constraints anymore and only one can be active at the same time. If you want to restrict a programme you should do it filling the start and end date fields inside itself.

How this affects you

If you’re starting with a new installation, you don’t need to worry about this. Don’t even bother reading this section; it’s for upgraders.

Please make sure of having a copy of your database before even started to look to the next steps.

If you have custom code or templates I recommend putting them in a safe place, special mention to local_settings.py files, your are going to need to move them to the correct folder.

Since this migration introduces timezone support, it’s necessary to set the timezone variable:

  1. Find your timezone in this list
  2. Go to the config folder and create if not exists a local_settings.py in the same directory than settings.py
  3. Add to the local settings the variable timezone, for example: TIME_ZONE = “Europe/Madrid”

Warning

Using an incorrect timezone will cause incorrect dates, you should set/override that setting in your local_settings.py.

Warning

If you have a database backend different to PostgreSQL you must convert your data from local time to UTC. Fortunately for you a migration was written to fix episode issue_dates, other dates will suffer alterations.

Warning

We strongly recommend to use PostgreSQL as backend. Other database engines will not be supported in the future.

You have to install bower before running the following commands in the project root folder:

bower install
pip install -r radioco/configs/base/requirements.txt
python manage.py collectstatic
python manage.py migrate

After the migration process you will find that there is a new calendar set to active that contains all your schedules and your other calendars have been renamed with the prefix “Legacy - ”. Feel free of delete these, they are not require anymore.

Note

Warning messages could appear during the migration process, it’s usually safe to ignore them.

Indices and tables