@ -1,9 +1,11 @@
 | 
			
		||||
from . import filters
 | 
			
		||||
from .article import ArticleAdmin
 | 
			
		||||
from .episode import DiffusionAdmin, EpisodeAdmin
 | 
			
		||||
from .diffusion import DiffusionAdmin
 | 
			
		||||
from .episode import EpisodeAdmin
 | 
			
		||||
from .log import LogAdmin
 | 
			
		||||
from .page import PageAdmin, StaticPageAdmin
 | 
			
		||||
from .program import ProgramAdmin, ScheduleAdmin, StreamAdmin
 | 
			
		||||
from .program import ProgramAdmin, StreamAdmin
 | 
			
		||||
from .schedule import ScheduleAdmin
 | 
			
		||||
from .sound import SoundAdmin, TrackAdmin
 | 
			
		||||
from .station import StationAdmin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										48
									
								
								aircox/admin/diffusion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								aircox/admin/diffusion.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
from aircox.models import Diffusion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("DiffusionBaseAdmin", "DiffusionAdmin", "DiffusionInline")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionBaseAdmin:
 | 
			
		||||
    fields = ("type", "start", "end", "schedule")
 | 
			
		||||
    readonly_fields = ("schedule",)
 | 
			
		||||
 | 
			
		||||
    def get_readonly_fields(self, request, obj=None):
 | 
			
		||||
        fields = super().get_readonly_fields(request, obj)
 | 
			
		||||
        if not request.user.has_perm("aircox_program.scheduling"):
 | 
			
		||||
            fields = fields + ("program", "start", "end")
 | 
			
		||||
        return [field for field in fields if field in self.fields]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Diffusion)
 | 
			
		||||
class DiffusionAdmin(DiffusionBaseAdmin, admin.ModelAdmin):
 | 
			
		||||
    def start_date(self, obj):
 | 
			
		||||
        return obj.local_start.strftime("%Y/%m/%d %H:%M")
 | 
			
		||||
 | 
			
		||||
    start_date.short_description = _("start")
 | 
			
		||||
 | 
			
		||||
    def end_date(self, obj):
 | 
			
		||||
        return obj.local_end.strftime("%H:%M")
 | 
			
		||||
 | 
			
		||||
    end_date.short_description = _("end")
 | 
			
		||||
 | 
			
		||||
    list_display = ("episode", "start_date", "end_date", "type", "initial")
 | 
			
		||||
    list_filter = ("type", "start", "program")
 | 
			
		||||
    list_editable = ("type",)
 | 
			
		||||
    ordering = ("-start", "id")
 | 
			
		||||
 | 
			
		||||
    fields = ("type", "start", "end", "initial", "program", "schedule")
 | 
			
		||||
    readonly_fields = ("schedule",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionInline(DiffusionBaseAdmin, admin.TabularInline):
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    fk_name = "episode"
 | 
			
		||||
    extra = 0
 | 
			
		||||
 | 
			
		||||
    def has_add_permission(self, request, obj):
 | 
			
		||||
        return request.user.has_perm("aircox_program.scheduling")
 | 
			
		||||
@ -1,52 +1,11 @@
 | 
			
		||||
from adminsortable2.admin import SortableAdminBase
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.forms import ModelForm
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
 | 
			
		||||
from ..models import Diffusion, Episode
 | 
			
		||||
from aircox.models import Episode
 | 
			
		||||
from .page import PageAdmin
 | 
			
		||||
from .sound import SoundInline, TrackInline
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionBaseAdmin:
 | 
			
		||||
    fields = ("type", "start", "end", "schedule")
 | 
			
		||||
    readonly_fields = ("schedule",)
 | 
			
		||||
 | 
			
		||||
    def get_readonly_fields(self, request, obj=None):
 | 
			
		||||
        fields = super().get_readonly_fields(request, obj)
 | 
			
		||||
        if not request.user.has_perm("aircox_program.scheduling"):
 | 
			
		||||
            fields = fields + ("program", "start", "end")
 | 
			
		||||
        return [field for field in fields if field in self.fields]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Diffusion)
 | 
			
		||||
class DiffusionAdmin(DiffusionBaseAdmin, admin.ModelAdmin):
 | 
			
		||||
    def start_date(self, obj):
 | 
			
		||||
        return obj.local_start.strftime("%Y/%m/%d %H:%M")
 | 
			
		||||
 | 
			
		||||
    start_date.short_description = _("start")
 | 
			
		||||
 | 
			
		||||
    def end_date(self, obj):
 | 
			
		||||
        return obj.local_end.strftime("%H:%M")
 | 
			
		||||
 | 
			
		||||
    end_date.short_description = _("end")
 | 
			
		||||
 | 
			
		||||
    list_display = ("episode", "start_date", "end_date", "type", "initial")
 | 
			
		||||
    list_filter = ("type", "start", "program")
 | 
			
		||||
    list_editable = ("type",)
 | 
			
		||||
    ordering = ("-start", "id")
 | 
			
		||||
 | 
			
		||||
    fields = ("type", "start", "end", "initial", "program", "schedule")
 | 
			
		||||
    readonly_fields = ("schedule",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionInline(DiffusionBaseAdmin, admin.TabularInline):
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    fk_name = "episode"
 | 
			
		||||
    extra = 0
 | 
			
		||||
 | 
			
		||||
    def has_add_permission(self, request, obj):
 | 
			
		||||
        return request.user.has_perm("aircox_program.scheduling")
 | 
			
		||||
from .diffusion import DiffusionInline
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EpisodeAdminForm(ModelForm):
 | 
			
		||||
 | 
			
		||||
@ -1,26 +1,12 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.forms import ModelForm
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from ..models import Program, Schedule, Stream
 | 
			
		||||
from aircox.models import Program, Schedule, Stream
 | 
			
		||||
from .page import PageAdmin
 | 
			
		||||
from .schedule import ScheduleInline
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# In order to simplify schedule_post_save algorithm, an existing schedule can't
 | 
			
		||||
# update the following fields: "frequency", "date"
 | 
			
		||||
class ScheduleInlineForm(ModelForm):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.initial:
 | 
			
		||||
            self.fields["date"].disabled = True
 | 
			
		||||
            self.fields["frequency"].disabled = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleInline(admin.TabularInline):
 | 
			
		||||
    model = Schedule
 | 
			
		||||
    form = ScheduleInlineForm
 | 
			
		||||
    readonly_fields = ("timezone",)
 | 
			
		||||
    extra = 1
 | 
			
		||||
__all__ = ("ProgramAdmin", "StreamInline", "StreamAdmin")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StreamInline(admin.TabularInline):
 | 
			
		||||
@ -58,36 +44,6 @@ class ProgramAdmin(PageAdmin):
 | 
			
		||||
        return fields
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Schedule)
 | 
			
		||||
class ScheduleAdmin(admin.ModelAdmin):
 | 
			
		||||
    def program_title(self, obj):
 | 
			
		||||
        return obj.program.title
 | 
			
		||||
 | 
			
		||||
    program_title.short_description = _("Program")
 | 
			
		||||
 | 
			
		||||
    def freq(self, obj):
 | 
			
		||||
        return obj.get_frequency_verbose()
 | 
			
		||||
 | 
			
		||||
    freq.short_description = _("Day")
 | 
			
		||||
 | 
			
		||||
    list_filter = ["frequency", "program"]
 | 
			
		||||
    list_display = [
 | 
			
		||||
        "program_title",
 | 
			
		||||
        "freq",
 | 
			
		||||
        "time",
 | 
			
		||||
        "timezone",
 | 
			
		||||
        "duration",
 | 
			
		||||
        "initial",
 | 
			
		||||
    ]
 | 
			
		||||
    list_editable = ["time", "duration", "initial"]
 | 
			
		||||
 | 
			
		||||
    def get_readonly_fields(self, request, obj=None):
 | 
			
		||||
        if obj:
 | 
			
		||||
            return ["program", "date", "frequency"]
 | 
			
		||||
        else:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Stream)
 | 
			
		||||
class StreamAdmin(admin.ModelAdmin):
 | 
			
		||||
    list_display = ("id", "program", "delay", "begin", "end")
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								aircox/admin/schedule.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								aircox/admin/schedule.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.forms import ModelForm
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from aircox.models import Schedule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("ScheduleInlineForm", "ScheduleInline", "ScheduleAdmin")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# In order to simplify schedule_post_save algorithm, an existing schedule can't
 | 
			
		||||
# update the following fields: "frequency", "date"
 | 
			
		||||
class ScheduleInlineForm(ModelForm):
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.initial:
 | 
			
		||||
            self.fields["date"].disabled = True
 | 
			
		||||
            self.fields["frequency"].disabled = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleInline(admin.TabularInline):
 | 
			
		||||
    model = Schedule
 | 
			
		||||
    form = ScheduleInlineForm
 | 
			
		||||
    readonly_fields = ("timezone",)
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Schedule)
 | 
			
		||||
class ScheduleAdmin(admin.ModelAdmin):
 | 
			
		||||
    def program_title(self, obj):
 | 
			
		||||
        return obj.program.title
 | 
			
		||||
 | 
			
		||||
    program_title.short_description = _("Program")
 | 
			
		||||
 | 
			
		||||
    def freq(self, obj):
 | 
			
		||||
        return obj.get_frequency_display()
 | 
			
		||||
 | 
			
		||||
    freq.short_description = _("Day")
 | 
			
		||||
 | 
			
		||||
    list_filter = ["frequency", "program"]
 | 
			
		||||
    list_display = [
 | 
			
		||||
        "program_title",
 | 
			
		||||
        "freq",
 | 
			
		||||
        "time",
 | 
			
		||||
        "timezone",
 | 
			
		||||
        "duration",
 | 
			
		||||
        "initial",
 | 
			
		||||
    ]
 | 
			
		||||
    list_editable = ["time", "duration", "initial"]
 | 
			
		||||
 | 
			
		||||
    def get_readonly_fields(self, request, obj=None):
 | 
			
		||||
        if obj:
 | 
			
		||||
            return ["program", "date", "frequency"]
 | 
			
		||||
        else:
 | 
			
		||||
            return []
 | 
			
		||||
							
								
								
									
										1722
									
								
								aircox/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1722
									
								
								aircox/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										17
									
								
								aircox/migrations/0002_auto_20200526_1516.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								aircox/migrations/0002_auto_20200526_1516.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
			
		||||
# Generated by Django 3.0.6 on 2020-05-26 13:16
 | 
			
		||||
 | 
			
		||||
from django.db import migrations
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0001_initial"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RenameField(
 | 
			
		||||
            model_name="staticpage",
 | 
			
		||||
            old_name="view",
 | 
			
		||||
            new_name="attach_to",
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										146
									
								
								aircox/migrations/0003_auto_20200530_1116.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								aircox/migrations/0003_auto_20200530_1116.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
			
		||||
# Generated by Django 3.0.6 on 2020-05-30 11:16
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
import filer.fields.image
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        migrations.swappable_dependency(settings.FILER_IMAGE_MODEL),
 | 
			
		||||
        ("aircox", "0002_auto_20200526_1516"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="log",
 | 
			
		||||
            options={"verbose_name": "Log", "verbose_name_plural": "Logs"},
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="page",
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Publication",
 | 
			
		||||
                "verbose_name_plural": "Publications",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="program",
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Program",
 | 
			
		||||
                "verbose_name_plural": "Programs",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="article",
 | 
			
		||||
            name="is_static",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="diffusion",
 | 
			
		||||
            name="schedule",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                to="aircox.Schedule",
 | 
			
		||||
                verbose_name="schedule",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="diffusion",
 | 
			
		||||
            name="initial",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                limit_choices_to={"initial__isnull": True},
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="rerun_set",
 | 
			
		||||
                to="aircox.Diffusion",
 | 
			
		||||
                verbose_name="rerun of",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="navitem",
 | 
			
		||||
            name="page",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                limit_choices_to={"attach_to__isnull": True},
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                to="aircox.StaticPage",
 | 
			
		||||
                verbose_name="page",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="frequency",
 | 
			
		||||
            field=models.SmallIntegerField(
 | 
			
		||||
                choices=[
 | 
			
		||||
                    (0, "ponctual"),
 | 
			
		||||
                    (1, "1st {day} of the month"),
 | 
			
		||||
                    (2, "2nd {day} of the month"),
 | 
			
		||||
                    (4, "3rd {day} of the month"),
 | 
			
		||||
                    (8, "4th {day} of the month"),
 | 
			
		||||
                    (16, "last {day} of the month"),
 | 
			
		||||
                    (5, "1st and 3rd {day} of the month"),
 | 
			
		||||
                    (10, "2nd and 4th {day} of the month"),
 | 
			
		||||
                    (31, "every {day}"),
 | 
			
		||||
                    (32, "one {day} on two"),
 | 
			
		||||
                ],
 | 
			
		||||
                verbose_name="frequency",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="initial",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                limit_choices_to={"initial__isnull": True},
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="rerun_set",
 | 
			
		||||
                to="aircox.Schedule",
 | 
			
		||||
                verbose_name="rerun of",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="staticpage",
 | 
			
		||||
            name="attach_to",
 | 
			
		||||
            field=models.SmallIntegerField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                choices=[
 | 
			
		||||
                    (0, "Home page"),
 | 
			
		||||
                    (1, "Diffusions page"),
 | 
			
		||||
                    (2, "Logs page"),
 | 
			
		||||
                    (3, "Programs list"),
 | 
			
		||||
                    (4, "Episodes list"),
 | 
			
		||||
                    (5, "Articles list"),
 | 
			
		||||
                ],
 | 
			
		||||
                help_text="display this page content to related element",
 | 
			
		||||
                null=True,
 | 
			
		||||
                verbose_name="attach to",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="station",
 | 
			
		||||
            name="default_cover",
 | 
			
		||||
            field=filer.fields.image.FilerImageField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.SET_NULL,
 | 
			
		||||
                related_name="+",
 | 
			
		||||
                to=settings.FILER_IMAGE_MODEL,
 | 
			
		||||
                verbose_name="Default pages' cover",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="track",
 | 
			
		||||
            name="timestamp",
 | 
			
		||||
            field=models.PositiveSmallIntegerField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                help_text="position (in seconds)",
 | 
			
		||||
                null=True,
 | 
			
		||||
                verbose_name="timestamp",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										59
									
								
								aircox/migrations/0004_auto_20200921_2356.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								aircox/migrations/0004_auto_20200921_2356.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
# Generated by Django 3.1.1 on 2020-09-21 23:56
 | 
			
		||||
 | 
			
		||||
import ckeditor_uploader.fields
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0003_auto_20200530_1116"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="comment",
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "Comment",
 | 
			
		||||
                "verbose_name_plural": "Comments",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="navitem",
 | 
			
		||||
            options={
 | 
			
		||||
                "ordering": ("order", "pk"),
 | 
			
		||||
                "verbose_name": "Menu item",
 | 
			
		||||
                "verbose_name_plural": "Menu items",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="embed",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="page",
 | 
			
		||||
            name="content",
 | 
			
		||||
            field=ckeditor_uploader.fields.RichTextUploadingField(
 | 
			
		||||
                blank=True, null=True, verbose_name="content"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="program",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                default=1,
 | 
			
		||||
                help_text="program related to it",
 | 
			
		||||
                on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                to="aircox.program",
 | 
			
		||||
                verbose_name="program",
 | 
			
		||||
            ),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="staticpage",
 | 
			
		||||
            name="content",
 | 
			
		||||
            field=ckeditor_uploader.fields.RichTextUploadingField(
 | 
			
		||||
                blank=True, null=True, verbose_name="content"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										839
									
								
								aircox/migrations/0005_auto_20220318_1205.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										839
									
								
								aircox/migrations/0005_auto_20220318_1205.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,839 @@
 | 
			
		||||
# Generated by Django 3.2.12 on 2022-03-18 12:05
 | 
			
		||||
 | 
			
		||||
import aircox.models.sound
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
import django.utils.timezone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0004_auto_20200921_2356"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.RemoveField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="path",
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="file",
 | 
			
		||||
            field=models.FileField(
 | 
			
		||||
                default="",
 | 
			
		||||
                upload_to=aircox.models.sound.Sound._upload_to,
 | 
			
		||||
                verbose_name="file",
 | 
			
		||||
            ),
 | 
			
		||||
            preserve_default=False,
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="category",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="comment",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="diffusion",
 | 
			
		||||
            name="end",
 | 
			
		||||
            field=models.DateTimeField(db_index=True, verbose_name="end"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="diffusion",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="diffusion",
 | 
			
		||||
            name="start",
 | 
			
		||||
            field=models.DateTimeField(db_index=True, verbose_name="start"),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="log",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="navitem",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="navitem",
 | 
			
		||||
            name="page",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                to="aircox.staticpage",
 | 
			
		||||
                verbose_name="page",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="page",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="port",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="timezone",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                choices=[
 | 
			
		||||
                    ("Africa/Abidjan", "Africa/Abidjan"),
 | 
			
		||||
                    ("Africa/Accra", "Africa/Accra"),
 | 
			
		||||
                    ("Africa/Addis_Ababa", "Africa/Addis_Ababa"),
 | 
			
		||||
                    ("Africa/Algiers", "Africa/Algiers"),
 | 
			
		||||
                    ("Africa/Asmara", "Africa/Asmara"),
 | 
			
		||||
                    ("Africa/Asmera", "Africa/Asmera"),
 | 
			
		||||
                    ("Africa/Bamako", "Africa/Bamako"),
 | 
			
		||||
                    ("Africa/Bangui", "Africa/Bangui"),
 | 
			
		||||
                    ("Africa/Banjul", "Africa/Banjul"),
 | 
			
		||||
                    ("Africa/Bissau", "Africa/Bissau"),
 | 
			
		||||
                    ("Africa/Blantyre", "Africa/Blantyre"),
 | 
			
		||||
                    ("Africa/Brazzaville", "Africa/Brazzaville"),
 | 
			
		||||
                    ("Africa/Bujumbura", "Africa/Bujumbura"),
 | 
			
		||||
                    ("Africa/Cairo", "Africa/Cairo"),
 | 
			
		||||
                    ("Africa/Casablanca", "Africa/Casablanca"),
 | 
			
		||||
                    ("Africa/Ceuta", "Africa/Ceuta"),
 | 
			
		||||
                    ("Africa/Conakry", "Africa/Conakry"),
 | 
			
		||||
                    ("Africa/Dakar", "Africa/Dakar"),
 | 
			
		||||
                    ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"),
 | 
			
		||||
                    ("Africa/Djibouti", "Africa/Djibouti"),
 | 
			
		||||
                    ("Africa/Douala", "Africa/Douala"),
 | 
			
		||||
                    ("Africa/El_Aaiun", "Africa/El_Aaiun"),
 | 
			
		||||
                    ("Africa/Freetown", "Africa/Freetown"),
 | 
			
		||||
                    ("Africa/Gaborone", "Africa/Gaborone"),
 | 
			
		||||
                    ("Africa/Harare", "Africa/Harare"),
 | 
			
		||||
                    ("Africa/Johannesburg", "Africa/Johannesburg"),
 | 
			
		||||
                    ("Africa/Juba", "Africa/Juba"),
 | 
			
		||||
                    ("Africa/Kampala", "Africa/Kampala"),
 | 
			
		||||
                    ("Africa/Khartoum", "Africa/Khartoum"),
 | 
			
		||||
                    ("Africa/Kigali", "Africa/Kigali"),
 | 
			
		||||
                    ("Africa/Kinshasa", "Africa/Kinshasa"),
 | 
			
		||||
                    ("Africa/Lagos", "Africa/Lagos"),
 | 
			
		||||
                    ("Africa/Libreville", "Africa/Libreville"),
 | 
			
		||||
                    ("Africa/Lome", "Africa/Lome"),
 | 
			
		||||
                    ("Africa/Luanda", "Africa/Luanda"),
 | 
			
		||||
                    ("Africa/Lubumbashi", "Africa/Lubumbashi"),
 | 
			
		||||
                    ("Africa/Lusaka", "Africa/Lusaka"),
 | 
			
		||||
                    ("Africa/Malabo", "Africa/Malabo"),
 | 
			
		||||
                    ("Africa/Maputo", "Africa/Maputo"),
 | 
			
		||||
                    ("Africa/Maseru", "Africa/Maseru"),
 | 
			
		||||
                    ("Africa/Mbabane", "Africa/Mbabane"),
 | 
			
		||||
                    ("Africa/Mogadishu", "Africa/Mogadishu"),
 | 
			
		||||
                    ("Africa/Monrovia", "Africa/Monrovia"),
 | 
			
		||||
                    ("Africa/Nairobi", "Africa/Nairobi"),
 | 
			
		||||
                    ("Africa/Ndjamena", "Africa/Ndjamena"),
 | 
			
		||||
                    ("Africa/Niamey", "Africa/Niamey"),
 | 
			
		||||
                    ("Africa/Nouakchott", "Africa/Nouakchott"),
 | 
			
		||||
                    ("Africa/Ouagadougou", "Africa/Ouagadougou"),
 | 
			
		||||
                    ("Africa/Porto-Novo", "Africa/Porto-Novo"),
 | 
			
		||||
                    ("Africa/Sao_Tome", "Africa/Sao_Tome"),
 | 
			
		||||
                    ("Africa/Timbuktu", "Africa/Timbuktu"),
 | 
			
		||||
                    ("Africa/Tripoli", "Africa/Tripoli"),
 | 
			
		||||
                    ("Africa/Tunis", "Africa/Tunis"),
 | 
			
		||||
                    ("Africa/Windhoek", "Africa/Windhoek"),
 | 
			
		||||
                    ("America/Adak", "America/Adak"),
 | 
			
		||||
                    ("America/Anchorage", "America/Anchorage"),
 | 
			
		||||
                    ("America/Anguilla", "America/Anguilla"),
 | 
			
		||||
                    ("America/Antigua", "America/Antigua"),
 | 
			
		||||
                    ("America/Araguaina", "America/Araguaina"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Buenos_Aires",
 | 
			
		||||
                        "America/Argentina/Buenos_Aires",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Catamarca",
 | 
			
		||||
                        "America/Argentina/Catamarca",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/ComodRivadavia",
 | 
			
		||||
                        "America/Argentina/ComodRivadavia",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Cordoba", "America/Argentina/Cordoba"),
 | 
			
		||||
                    ("America/Argentina/Jujuy", "America/Argentina/Jujuy"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/La_Rioja",
 | 
			
		||||
                        "America/Argentina/La_Rioja",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Mendoza", "America/Argentina/Mendoza"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Rio_Gallegos",
 | 
			
		||||
                        "America/Argentina/Rio_Gallegos",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Salta", "America/Argentina/Salta"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/San_Juan",
 | 
			
		||||
                        "America/Argentina/San_Juan",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/San_Luis",
 | 
			
		||||
                        "America/Argentina/San_Luis",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Tucuman", "America/Argentina/Tucuman"),
 | 
			
		||||
                    ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"),
 | 
			
		||||
                    ("America/Aruba", "America/Aruba"),
 | 
			
		||||
                    ("America/Asuncion", "America/Asuncion"),
 | 
			
		||||
                    ("America/Atikokan", "America/Atikokan"),
 | 
			
		||||
                    ("America/Atka", "America/Atka"),
 | 
			
		||||
                    ("America/Bahia", "America/Bahia"),
 | 
			
		||||
                    ("America/Bahia_Banderas", "America/Bahia_Banderas"),
 | 
			
		||||
                    ("America/Barbados", "America/Barbados"),
 | 
			
		||||
                    ("America/Belem", "America/Belem"),
 | 
			
		||||
                    ("America/Belize", "America/Belize"),
 | 
			
		||||
                    ("America/Blanc-Sablon", "America/Blanc-Sablon"),
 | 
			
		||||
                    ("America/Boa_Vista", "America/Boa_Vista"),
 | 
			
		||||
                    ("America/Bogota", "America/Bogota"),
 | 
			
		||||
                    ("America/Boise", "America/Boise"),
 | 
			
		||||
                    ("America/Buenos_Aires", "America/Buenos_Aires"),
 | 
			
		||||
                    ("America/Cambridge_Bay", "America/Cambridge_Bay"),
 | 
			
		||||
                    ("America/Campo_Grande", "America/Campo_Grande"),
 | 
			
		||||
                    ("America/Cancun", "America/Cancun"),
 | 
			
		||||
                    ("America/Caracas", "America/Caracas"),
 | 
			
		||||
                    ("America/Catamarca", "America/Catamarca"),
 | 
			
		||||
                    ("America/Cayenne", "America/Cayenne"),
 | 
			
		||||
                    ("America/Cayman", "America/Cayman"),
 | 
			
		||||
                    ("America/Chicago", "America/Chicago"),
 | 
			
		||||
                    ("America/Chihuahua", "America/Chihuahua"),
 | 
			
		||||
                    ("America/Coral_Harbour", "America/Coral_Harbour"),
 | 
			
		||||
                    ("America/Cordoba", "America/Cordoba"),
 | 
			
		||||
                    ("America/Costa_Rica", "America/Costa_Rica"),
 | 
			
		||||
                    ("America/Creston", "America/Creston"),
 | 
			
		||||
                    ("America/Cuiaba", "America/Cuiaba"),
 | 
			
		||||
                    ("America/Curacao", "America/Curacao"),
 | 
			
		||||
                    ("America/Danmarkshavn", "America/Danmarkshavn"),
 | 
			
		||||
                    ("America/Dawson", "America/Dawson"),
 | 
			
		||||
                    ("America/Dawson_Creek", "America/Dawson_Creek"),
 | 
			
		||||
                    ("America/Denver", "America/Denver"),
 | 
			
		||||
                    ("America/Detroit", "America/Detroit"),
 | 
			
		||||
                    ("America/Dominica", "America/Dominica"),
 | 
			
		||||
                    ("America/Edmonton", "America/Edmonton"),
 | 
			
		||||
                    ("America/Eirunepe", "America/Eirunepe"),
 | 
			
		||||
                    ("America/El_Salvador", "America/El_Salvador"),
 | 
			
		||||
                    ("America/Ensenada", "America/Ensenada"),
 | 
			
		||||
                    ("America/Fort_Nelson", "America/Fort_Nelson"),
 | 
			
		||||
                    ("America/Fort_Wayne", "America/Fort_Wayne"),
 | 
			
		||||
                    ("America/Fortaleza", "America/Fortaleza"),
 | 
			
		||||
                    ("America/Glace_Bay", "America/Glace_Bay"),
 | 
			
		||||
                    ("America/Godthab", "America/Godthab"),
 | 
			
		||||
                    ("America/Goose_Bay", "America/Goose_Bay"),
 | 
			
		||||
                    ("America/Grand_Turk", "America/Grand_Turk"),
 | 
			
		||||
                    ("America/Grenada", "America/Grenada"),
 | 
			
		||||
                    ("America/Guadeloupe", "America/Guadeloupe"),
 | 
			
		||||
                    ("America/Guatemala", "America/Guatemala"),
 | 
			
		||||
                    ("America/Guayaquil", "America/Guayaquil"),
 | 
			
		||||
                    ("America/Guyana", "America/Guyana"),
 | 
			
		||||
                    ("America/Halifax", "America/Halifax"),
 | 
			
		||||
                    ("America/Havana", "America/Havana"),
 | 
			
		||||
                    ("America/Hermosillo", "America/Hermosillo"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Indiana/Indianapolis",
 | 
			
		||||
                        "America/Indiana/Indianapolis",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Indiana/Knox", "America/Indiana/Knox"),
 | 
			
		||||
                    ("America/Indiana/Marengo", "America/Indiana/Marengo"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Indiana/Petersburg",
 | 
			
		||||
                        "America/Indiana/Petersburg",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Indiana/Tell_City", "America/Indiana/Tell_City"),
 | 
			
		||||
                    ("America/Indiana/Vevay", "America/Indiana/Vevay"),
 | 
			
		||||
                    ("America/Indiana/Vincennes", "America/Indiana/Vincennes"),
 | 
			
		||||
                    ("America/Indiana/Winamac", "America/Indiana/Winamac"),
 | 
			
		||||
                    ("America/Indianapolis", "America/Indianapolis"),
 | 
			
		||||
                    ("America/Inuvik", "America/Inuvik"),
 | 
			
		||||
                    ("America/Iqaluit", "America/Iqaluit"),
 | 
			
		||||
                    ("America/Jamaica", "America/Jamaica"),
 | 
			
		||||
                    ("America/Jujuy", "America/Jujuy"),
 | 
			
		||||
                    ("America/Juneau", "America/Juneau"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Kentucky/Louisville",
 | 
			
		||||
                        "America/Kentucky/Louisville",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Kentucky/Monticello",
 | 
			
		||||
                        "America/Kentucky/Monticello",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Knox_IN", "America/Knox_IN"),
 | 
			
		||||
                    ("America/Kralendijk", "America/Kralendijk"),
 | 
			
		||||
                    ("America/La_Paz", "America/La_Paz"),
 | 
			
		||||
                    ("America/Lima", "America/Lima"),
 | 
			
		||||
                    ("America/Los_Angeles", "America/Los_Angeles"),
 | 
			
		||||
                    ("America/Louisville", "America/Louisville"),
 | 
			
		||||
                    ("America/Lower_Princes", "America/Lower_Princes"),
 | 
			
		||||
                    ("America/Maceio", "America/Maceio"),
 | 
			
		||||
                    ("America/Managua", "America/Managua"),
 | 
			
		||||
                    ("America/Manaus", "America/Manaus"),
 | 
			
		||||
                    ("America/Marigot", "America/Marigot"),
 | 
			
		||||
                    ("America/Martinique", "America/Martinique"),
 | 
			
		||||
                    ("America/Matamoros", "America/Matamoros"),
 | 
			
		||||
                    ("America/Mazatlan", "America/Mazatlan"),
 | 
			
		||||
                    ("America/Mendoza", "America/Mendoza"),
 | 
			
		||||
                    ("America/Menominee", "America/Menominee"),
 | 
			
		||||
                    ("America/Merida", "America/Merida"),
 | 
			
		||||
                    ("America/Metlakatla", "America/Metlakatla"),
 | 
			
		||||
                    ("America/Mexico_City", "America/Mexico_City"),
 | 
			
		||||
                    ("America/Miquelon", "America/Miquelon"),
 | 
			
		||||
                    ("America/Moncton", "America/Moncton"),
 | 
			
		||||
                    ("America/Monterrey", "America/Monterrey"),
 | 
			
		||||
                    ("America/Montevideo", "America/Montevideo"),
 | 
			
		||||
                    ("America/Montreal", "America/Montreal"),
 | 
			
		||||
                    ("America/Montserrat", "America/Montserrat"),
 | 
			
		||||
                    ("America/Nassau", "America/Nassau"),
 | 
			
		||||
                    ("America/New_York", "America/New_York"),
 | 
			
		||||
                    ("America/Nipigon", "America/Nipigon"),
 | 
			
		||||
                    ("America/Nome", "America/Nome"),
 | 
			
		||||
                    ("America/Noronha", "America/Noronha"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/Beulah",
 | 
			
		||||
                        "America/North_Dakota/Beulah",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/Center",
 | 
			
		||||
                        "America/North_Dakota/Center",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/New_Salem",
 | 
			
		||||
                        "America/North_Dakota/New_Salem",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Nuuk", "America/Nuuk"),
 | 
			
		||||
                    ("America/Ojinaga", "America/Ojinaga"),
 | 
			
		||||
                    ("America/Panama", "America/Panama"),
 | 
			
		||||
                    ("America/Pangnirtung", "America/Pangnirtung"),
 | 
			
		||||
                    ("America/Paramaribo", "America/Paramaribo"),
 | 
			
		||||
                    ("America/Phoenix", "America/Phoenix"),
 | 
			
		||||
                    ("America/Port-au-Prince", "America/Port-au-Prince"),
 | 
			
		||||
                    ("America/Port_of_Spain", "America/Port_of_Spain"),
 | 
			
		||||
                    ("America/Porto_Acre", "America/Porto_Acre"),
 | 
			
		||||
                    ("America/Porto_Velho", "America/Porto_Velho"),
 | 
			
		||||
                    ("America/Puerto_Rico", "America/Puerto_Rico"),
 | 
			
		||||
                    ("America/Punta_Arenas", "America/Punta_Arenas"),
 | 
			
		||||
                    ("America/Rainy_River", "America/Rainy_River"),
 | 
			
		||||
                    ("America/Rankin_Inlet", "America/Rankin_Inlet"),
 | 
			
		||||
                    ("America/Recife", "America/Recife"),
 | 
			
		||||
                    ("America/Regina", "America/Regina"),
 | 
			
		||||
                    ("America/Resolute", "America/Resolute"),
 | 
			
		||||
                    ("America/Rio_Branco", "America/Rio_Branco"),
 | 
			
		||||
                    ("America/Rosario", "America/Rosario"),
 | 
			
		||||
                    ("America/Santa_Isabel", "America/Santa_Isabel"),
 | 
			
		||||
                    ("America/Santarem", "America/Santarem"),
 | 
			
		||||
                    ("America/Santiago", "America/Santiago"),
 | 
			
		||||
                    ("America/Santo_Domingo", "America/Santo_Domingo"),
 | 
			
		||||
                    ("America/Sao_Paulo", "America/Sao_Paulo"),
 | 
			
		||||
                    ("America/Scoresbysund", "America/Scoresbysund"),
 | 
			
		||||
                    ("America/Shiprock", "America/Shiprock"),
 | 
			
		||||
                    ("America/Sitka", "America/Sitka"),
 | 
			
		||||
                    ("America/St_Barthelemy", "America/St_Barthelemy"),
 | 
			
		||||
                    ("America/St_Johns", "America/St_Johns"),
 | 
			
		||||
                    ("America/St_Kitts", "America/St_Kitts"),
 | 
			
		||||
                    ("America/St_Lucia", "America/St_Lucia"),
 | 
			
		||||
                    ("America/St_Thomas", "America/St_Thomas"),
 | 
			
		||||
                    ("America/St_Vincent", "America/St_Vincent"),
 | 
			
		||||
                    ("America/Swift_Current", "America/Swift_Current"),
 | 
			
		||||
                    ("America/Tegucigalpa", "America/Tegucigalpa"),
 | 
			
		||||
                    ("America/Thule", "America/Thule"),
 | 
			
		||||
                    ("America/Thunder_Bay", "America/Thunder_Bay"),
 | 
			
		||||
                    ("America/Tijuana", "America/Tijuana"),
 | 
			
		||||
                    ("America/Toronto", "America/Toronto"),
 | 
			
		||||
                    ("America/Tortola", "America/Tortola"),
 | 
			
		||||
                    ("America/Vancouver", "America/Vancouver"),
 | 
			
		||||
                    ("America/Virgin", "America/Virgin"),
 | 
			
		||||
                    ("America/Whitehorse", "America/Whitehorse"),
 | 
			
		||||
                    ("America/Winnipeg", "America/Winnipeg"),
 | 
			
		||||
                    ("America/Yakutat", "America/Yakutat"),
 | 
			
		||||
                    ("America/Yellowknife", "America/Yellowknife"),
 | 
			
		||||
                    ("Antarctica/Casey", "Antarctica/Casey"),
 | 
			
		||||
                    ("Antarctica/Davis", "Antarctica/Davis"),
 | 
			
		||||
                    ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"),
 | 
			
		||||
                    ("Antarctica/Macquarie", "Antarctica/Macquarie"),
 | 
			
		||||
                    ("Antarctica/Mawson", "Antarctica/Mawson"),
 | 
			
		||||
                    ("Antarctica/McMurdo", "Antarctica/McMurdo"),
 | 
			
		||||
                    ("Antarctica/Palmer", "Antarctica/Palmer"),
 | 
			
		||||
                    ("Antarctica/Rothera", "Antarctica/Rothera"),
 | 
			
		||||
                    ("Antarctica/South_Pole", "Antarctica/South_Pole"),
 | 
			
		||||
                    ("Antarctica/Syowa", "Antarctica/Syowa"),
 | 
			
		||||
                    ("Antarctica/Troll", "Antarctica/Troll"),
 | 
			
		||||
                    ("Antarctica/Vostok", "Antarctica/Vostok"),
 | 
			
		||||
                    ("Arctic/Longyearbyen", "Arctic/Longyearbyen"),
 | 
			
		||||
                    ("Asia/Aden", "Asia/Aden"),
 | 
			
		||||
                    ("Asia/Almaty", "Asia/Almaty"),
 | 
			
		||||
                    ("Asia/Amman", "Asia/Amman"),
 | 
			
		||||
                    ("Asia/Anadyr", "Asia/Anadyr"),
 | 
			
		||||
                    ("Asia/Aqtau", "Asia/Aqtau"),
 | 
			
		||||
                    ("Asia/Aqtobe", "Asia/Aqtobe"),
 | 
			
		||||
                    ("Asia/Ashgabat", "Asia/Ashgabat"),
 | 
			
		||||
                    ("Asia/Ashkhabad", "Asia/Ashkhabad"),
 | 
			
		||||
                    ("Asia/Atyrau", "Asia/Atyrau"),
 | 
			
		||||
                    ("Asia/Baghdad", "Asia/Baghdad"),
 | 
			
		||||
                    ("Asia/Bahrain", "Asia/Bahrain"),
 | 
			
		||||
                    ("Asia/Baku", "Asia/Baku"),
 | 
			
		||||
                    ("Asia/Bangkok", "Asia/Bangkok"),
 | 
			
		||||
                    ("Asia/Barnaul", "Asia/Barnaul"),
 | 
			
		||||
                    ("Asia/Beirut", "Asia/Beirut"),
 | 
			
		||||
                    ("Asia/Bishkek", "Asia/Bishkek"),
 | 
			
		||||
                    ("Asia/Brunei", "Asia/Brunei"),
 | 
			
		||||
                    ("Asia/Calcutta", "Asia/Calcutta"),
 | 
			
		||||
                    ("Asia/Chita", "Asia/Chita"),
 | 
			
		||||
                    ("Asia/Choibalsan", "Asia/Choibalsan"),
 | 
			
		||||
                    ("Asia/Chongqing", "Asia/Chongqing"),
 | 
			
		||||
                    ("Asia/Chungking", "Asia/Chungking"),
 | 
			
		||||
                    ("Asia/Colombo", "Asia/Colombo"),
 | 
			
		||||
                    ("Asia/Dacca", "Asia/Dacca"),
 | 
			
		||||
                    ("Asia/Damascus", "Asia/Damascus"),
 | 
			
		||||
                    ("Asia/Dhaka", "Asia/Dhaka"),
 | 
			
		||||
                    ("Asia/Dili", "Asia/Dili"),
 | 
			
		||||
                    ("Asia/Dubai", "Asia/Dubai"),
 | 
			
		||||
                    ("Asia/Dushanbe", "Asia/Dushanbe"),
 | 
			
		||||
                    ("Asia/Famagusta", "Asia/Famagusta"),
 | 
			
		||||
                    ("Asia/Gaza", "Asia/Gaza"),
 | 
			
		||||
                    ("Asia/Harbin", "Asia/Harbin"),
 | 
			
		||||
                    ("Asia/Hebron", "Asia/Hebron"),
 | 
			
		||||
                    ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"),
 | 
			
		||||
                    ("Asia/Hong_Kong", "Asia/Hong_Kong"),
 | 
			
		||||
                    ("Asia/Hovd", "Asia/Hovd"),
 | 
			
		||||
                    ("Asia/Irkutsk", "Asia/Irkutsk"),
 | 
			
		||||
                    ("Asia/Istanbul", "Asia/Istanbul"),
 | 
			
		||||
                    ("Asia/Jakarta", "Asia/Jakarta"),
 | 
			
		||||
                    ("Asia/Jayapura", "Asia/Jayapura"),
 | 
			
		||||
                    ("Asia/Jerusalem", "Asia/Jerusalem"),
 | 
			
		||||
                    ("Asia/Kabul", "Asia/Kabul"),
 | 
			
		||||
                    ("Asia/Kamchatka", "Asia/Kamchatka"),
 | 
			
		||||
                    ("Asia/Karachi", "Asia/Karachi"),
 | 
			
		||||
                    ("Asia/Kashgar", "Asia/Kashgar"),
 | 
			
		||||
                    ("Asia/Kathmandu", "Asia/Kathmandu"),
 | 
			
		||||
                    ("Asia/Katmandu", "Asia/Katmandu"),
 | 
			
		||||
                    ("Asia/Khandyga", "Asia/Khandyga"),
 | 
			
		||||
                    ("Asia/Kolkata", "Asia/Kolkata"),
 | 
			
		||||
                    ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"),
 | 
			
		||||
                    ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"),
 | 
			
		||||
                    ("Asia/Kuching", "Asia/Kuching"),
 | 
			
		||||
                    ("Asia/Kuwait", "Asia/Kuwait"),
 | 
			
		||||
                    ("Asia/Macao", "Asia/Macao"),
 | 
			
		||||
                    ("Asia/Macau", "Asia/Macau"),
 | 
			
		||||
                    ("Asia/Magadan", "Asia/Magadan"),
 | 
			
		||||
                    ("Asia/Makassar", "Asia/Makassar"),
 | 
			
		||||
                    ("Asia/Manila", "Asia/Manila"),
 | 
			
		||||
                    ("Asia/Muscat", "Asia/Muscat"),
 | 
			
		||||
                    ("Asia/Nicosia", "Asia/Nicosia"),
 | 
			
		||||
                    ("Asia/Novokuznetsk", "Asia/Novokuznetsk"),
 | 
			
		||||
                    ("Asia/Novosibirsk", "Asia/Novosibirsk"),
 | 
			
		||||
                    ("Asia/Omsk", "Asia/Omsk"),
 | 
			
		||||
                    ("Asia/Oral", "Asia/Oral"),
 | 
			
		||||
                    ("Asia/Phnom_Penh", "Asia/Phnom_Penh"),
 | 
			
		||||
                    ("Asia/Pontianak", "Asia/Pontianak"),
 | 
			
		||||
                    ("Asia/Pyongyang", "Asia/Pyongyang"),
 | 
			
		||||
                    ("Asia/Qatar", "Asia/Qatar"),
 | 
			
		||||
                    ("Asia/Qostanay", "Asia/Qostanay"),
 | 
			
		||||
                    ("Asia/Qyzylorda", "Asia/Qyzylorda"),
 | 
			
		||||
                    ("Asia/Rangoon", "Asia/Rangoon"),
 | 
			
		||||
                    ("Asia/Riyadh", "Asia/Riyadh"),
 | 
			
		||||
                    ("Asia/Saigon", "Asia/Saigon"),
 | 
			
		||||
                    ("Asia/Sakhalin", "Asia/Sakhalin"),
 | 
			
		||||
                    ("Asia/Samarkand", "Asia/Samarkand"),
 | 
			
		||||
                    ("Asia/Seoul", "Asia/Seoul"),
 | 
			
		||||
                    ("Asia/Shanghai", "Asia/Shanghai"),
 | 
			
		||||
                    ("Asia/Singapore", "Asia/Singapore"),
 | 
			
		||||
                    ("Asia/Srednekolymsk", "Asia/Srednekolymsk"),
 | 
			
		||||
                    ("Asia/Taipei", "Asia/Taipei"),
 | 
			
		||||
                    ("Asia/Tashkent", "Asia/Tashkent"),
 | 
			
		||||
                    ("Asia/Tbilisi", "Asia/Tbilisi"),
 | 
			
		||||
                    ("Asia/Tehran", "Asia/Tehran"),
 | 
			
		||||
                    ("Asia/Tel_Aviv", "Asia/Tel_Aviv"),
 | 
			
		||||
                    ("Asia/Thimbu", "Asia/Thimbu"),
 | 
			
		||||
                    ("Asia/Thimphu", "Asia/Thimphu"),
 | 
			
		||||
                    ("Asia/Tokyo", "Asia/Tokyo"),
 | 
			
		||||
                    ("Asia/Tomsk", "Asia/Tomsk"),
 | 
			
		||||
                    ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"),
 | 
			
		||||
                    ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"),
 | 
			
		||||
                    ("Asia/Ulan_Bator", "Asia/Ulan_Bator"),
 | 
			
		||||
                    ("Asia/Urumqi", "Asia/Urumqi"),
 | 
			
		||||
                    ("Asia/Ust-Nera", "Asia/Ust-Nera"),
 | 
			
		||||
                    ("Asia/Vientiane", "Asia/Vientiane"),
 | 
			
		||||
                    ("Asia/Vladivostok", "Asia/Vladivostok"),
 | 
			
		||||
                    ("Asia/Yakutsk", "Asia/Yakutsk"),
 | 
			
		||||
                    ("Asia/Yangon", "Asia/Yangon"),
 | 
			
		||||
                    ("Asia/Yekaterinburg", "Asia/Yekaterinburg"),
 | 
			
		||||
                    ("Asia/Yerevan", "Asia/Yerevan"),
 | 
			
		||||
                    ("Atlantic/Azores", "Atlantic/Azores"),
 | 
			
		||||
                    ("Atlantic/Bermuda", "Atlantic/Bermuda"),
 | 
			
		||||
                    ("Atlantic/Canary", "Atlantic/Canary"),
 | 
			
		||||
                    ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"),
 | 
			
		||||
                    ("Atlantic/Faeroe", "Atlantic/Faeroe"),
 | 
			
		||||
                    ("Atlantic/Faroe", "Atlantic/Faroe"),
 | 
			
		||||
                    ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"),
 | 
			
		||||
                    ("Atlantic/Madeira", "Atlantic/Madeira"),
 | 
			
		||||
                    ("Atlantic/Reykjavik", "Atlantic/Reykjavik"),
 | 
			
		||||
                    ("Atlantic/South_Georgia", "Atlantic/South_Georgia"),
 | 
			
		||||
                    ("Atlantic/St_Helena", "Atlantic/St_Helena"),
 | 
			
		||||
                    ("Atlantic/Stanley", "Atlantic/Stanley"),
 | 
			
		||||
                    ("Australia/ACT", "Australia/ACT"),
 | 
			
		||||
                    ("Australia/Adelaide", "Australia/Adelaide"),
 | 
			
		||||
                    ("Australia/Brisbane", "Australia/Brisbane"),
 | 
			
		||||
                    ("Australia/Broken_Hill", "Australia/Broken_Hill"),
 | 
			
		||||
                    ("Australia/Canberra", "Australia/Canberra"),
 | 
			
		||||
                    ("Australia/Currie", "Australia/Currie"),
 | 
			
		||||
                    ("Australia/Darwin", "Australia/Darwin"),
 | 
			
		||||
                    ("Australia/Eucla", "Australia/Eucla"),
 | 
			
		||||
                    ("Australia/Hobart", "Australia/Hobart"),
 | 
			
		||||
                    ("Australia/LHI", "Australia/LHI"),
 | 
			
		||||
                    ("Australia/Lindeman", "Australia/Lindeman"),
 | 
			
		||||
                    ("Australia/Lord_Howe", "Australia/Lord_Howe"),
 | 
			
		||||
                    ("Australia/Melbourne", "Australia/Melbourne"),
 | 
			
		||||
                    ("Australia/NSW", "Australia/NSW"),
 | 
			
		||||
                    ("Australia/North", "Australia/North"),
 | 
			
		||||
                    ("Australia/Perth", "Australia/Perth"),
 | 
			
		||||
                    ("Australia/Queensland", "Australia/Queensland"),
 | 
			
		||||
                    ("Australia/South", "Australia/South"),
 | 
			
		||||
                    ("Australia/Sydney", "Australia/Sydney"),
 | 
			
		||||
                    ("Australia/Tasmania", "Australia/Tasmania"),
 | 
			
		||||
                    ("Australia/Victoria", "Australia/Victoria"),
 | 
			
		||||
                    ("Australia/West", "Australia/West"),
 | 
			
		||||
                    ("Australia/Yancowinna", "Australia/Yancowinna"),
 | 
			
		||||
                    ("Brazil/Acre", "Brazil/Acre"),
 | 
			
		||||
                    ("Brazil/DeNoronha", "Brazil/DeNoronha"),
 | 
			
		||||
                    ("Brazil/East", "Brazil/East"),
 | 
			
		||||
                    ("Brazil/West", "Brazil/West"),
 | 
			
		||||
                    ("CET", "CET"),
 | 
			
		||||
                    ("CST6CDT", "CST6CDT"),
 | 
			
		||||
                    ("Canada/Atlantic", "Canada/Atlantic"),
 | 
			
		||||
                    ("Canada/Central", "Canada/Central"),
 | 
			
		||||
                    ("Canada/Eastern", "Canada/Eastern"),
 | 
			
		||||
                    ("Canada/Mountain", "Canada/Mountain"),
 | 
			
		||||
                    ("Canada/Newfoundland", "Canada/Newfoundland"),
 | 
			
		||||
                    ("Canada/Pacific", "Canada/Pacific"),
 | 
			
		||||
                    ("Canada/Saskatchewan", "Canada/Saskatchewan"),
 | 
			
		||||
                    ("Canada/Yukon", "Canada/Yukon"),
 | 
			
		||||
                    ("Chile/Continental", "Chile/Continental"),
 | 
			
		||||
                    ("Chile/EasterIsland", "Chile/EasterIsland"),
 | 
			
		||||
                    ("Cuba", "Cuba"),
 | 
			
		||||
                    ("EET", "EET"),
 | 
			
		||||
                    ("EST", "EST"),
 | 
			
		||||
                    ("EST5EDT", "EST5EDT"),
 | 
			
		||||
                    ("Egypt", "Egypt"),
 | 
			
		||||
                    ("Eire", "Eire"),
 | 
			
		||||
                    ("Etc/GMT", "Etc/GMT"),
 | 
			
		||||
                    ("Etc/GMT+0", "Etc/GMT+0"),
 | 
			
		||||
                    ("Etc/GMT+1", "Etc/GMT+1"),
 | 
			
		||||
                    ("Etc/GMT+10", "Etc/GMT+10"),
 | 
			
		||||
                    ("Etc/GMT+11", "Etc/GMT+11"),
 | 
			
		||||
                    ("Etc/GMT+12", "Etc/GMT+12"),
 | 
			
		||||
                    ("Etc/GMT+2", "Etc/GMT+2"),
 | 
			
		||||
                    ("Etc/GMT+3", "Etc/GMT+3"),
 | 
			
		||||
                    ("Etc/GMT+4", "Etc/GMT+4"),
 | 
			
		||||
                    ("Etc/GMT+5", "Etc/GMT+5"),
 | 
			
		||||
                    ("Etc/GMT+6", "Etc/GMT+6"),
 | 
			
		||||
                    ("Etc/GMT+7", "Etc/GMT+7"),
 | 
			
		||||
                    ("Etc/GMT+8", "Etc/GMT+8"),
 | 
			
		||||
                    ("Etc/GMT+9", "Etc/GMT+9"),
 | 
			
		||||
                    ("Etc/GMT-0", "Etc/GMT-0"),
 | 
			
		||||
                    ("Etc/GMT-1", "Etc/GMT-1"),
 | 
			
		||||
                    ("Etc/GMT-10", "Etc/GMT-10"),
 | 
			
		||||
                    ("Etc/GMT-11", "Etc/GMT-11"),
 | 
			
		||||
                    ("Etc/GMT-12", "Etc/GMT-12"),
 | 
			
		||||
                    ("Etc/GMT-13", "Etc/GMT-13"),
 | 
			
		||||
                    ("Etc/GMT-14", "Etc/GMT-14"),
 | 
			
		||||
                    ("Etc/GMT-2", "Etc/GMT-2"),
 | 
			
		||||
                    ("Etc/GMT-3", "Etc/GMT-3"),
 | 
			
		||||
                    ("Etc/GMT-4", "Etc/GMT-4"),
 | 
			
		||||
                    ("Etc/GMT-5", "Etc/GMT-5"),
 | 
			
		||||
                    ("Etc/GMT-6", "Etc/GMT-6"),
 | 
			
		||||
                    ("Etc/GMT-7", "Etc/GMT-7"),
 | 
			
		||||
                    ("Etc/GMT-8", "Etc/GMT-8"),
 | 
			
		||||
                    ("Etc/GMT-9", "Etc/GMT-9"),
 | 
			
		||||
                    ("Etc/GMT0", "Etc/GMT0"),
 | 
			
		||||
                    ("Etc/Greenwich", "Etc/Greenwich"),
 | 
			
		||||
                    ("Etc/UCT", "Etc/UCT"),
 | 
			
		||||
                    ("Etc/UTC", "Etc/UTC"),
 | 
			
		||||
                    ("Etc/Universal", "Etc/Universal"),
 | 
			
		||||
                    ("Etc/Zulu", "Etc/Zulu"),
 | 
			
		||||
                    ("Europe/Amsterdam", "Europe/Amsterdam"),
 | 
			
		||||
                    ("Europe/Andorra", "Europe/Andorra"),
 | 
			
		||||
                    ("Europe/Astrakhan", "Europe/Astrakhan"),
 | 
			
		||||
                    ("Europe/Athens", "Europe/Athens"),
 | 
			
		||||
                    ("Europe/Belfast", "Europe/Belfast"),
 | 
			
		||||
                    ("Europe/Belgrade", "Europe/Belgrade"),
 | 
			
		||||
                    ("Europe/Berlin", "Europe/Berlin"),
 | 
			
		||||
                    ("Europe/Bratislava", "Europe/Bratislava"),
 | 
			
		||||
                    ("Europe/Brussels", "Europe/Brussels"),
 | 
			
		||||
                    ("Europe/Bucharest", "Europe/Bucharest"),
 | 
			
		||||
                    ("Europe/Budapest", "Europe/Budapest"),
 | 
			
		||||
                    ("Europe/Busingen", "Europe/Busingen"),
 | 
			
		||||
                    ("Europe/Chisinau", "Europe/Chisinau"),
 | 
			
		||||
                    ("Europe/Copenhagen", "Europe/Copenhagen"),
 | 
			
		||||
                    ("Europe/Dublin", "Europe/Dublin"),
 | 
			
		||||
                    ("Europe/Gibraltar", "Europe/Gibraltar"),
 | 
			
		||||
                    ("Europe/Guernsey", "Europe/Guernsey"),
 | 
			
		||||
                    ("Europe/Helsinki", "Europe/Helsinki"),
 | 
			
		||||
                    ("Europe/Isle_of_Man", "Europe/Isle_of_Man"),
 | 
			
		||||
                    ("Europe/Istanbul", "Europe/Istanbul"),
 | 
			
		||||
                    ("Europe/Jersey", "Europe/Jersey"),
 | 
			
		||||
                    ("Europe/Kaliningrad", "Europe/Kaliningrad"),
 | 
			
		||||
                    ("Europe/Kiev", "Europe/Kiev"),
 | 
			
		||||
                    ("Europe/Kirov", "Europe/Kirov"),
 | 
			
		||||
                    ("Europe/Lisbon", "Europe/Lisbon"),
 | 
			
		||||
                    ("Europe/Ljubljana", "Europe/Ljubljana"),
 | 
			
		||||
                    ("Europe/London", "Europe/London"),
 | 
			
		||||
                    ("Europe/Luxembourg", "Europe/Luxembourg"),
 | 
			
		||||
                    ("Europe/Madrid", "Europe/Madrid"),
 | 
			
		||||
                    ("Europe/Malta", "Europe/Malta"),
 | 
			
		||||
                    ("Europe/Mariehamn", "Europe/Mariehamn"),
 | 
			
		||||
                    ("Europe/Minsk", "Europe/Minsk"),
 | 
			
		||||
                    ("Europe/Monaco", "Europe/Monaco"),
 | 
			
		||||
                    ("Europe/Moscow", "Europe/Moscow"),
 | 
			
		||||
                    ("Europe/Nicosia", "Europe/Nicosia"),
 | 
			
		||||
                    ("Europe/Oslo", "Europe/Oslo"),
 | 
			
		||||
                    ("Europe/Paris", "Europe/Paris"),
 | 
			
		||||
                    ("Europe/Podgorica", "Europe/Podgorica"),
 | 
			
		||||
                    ("Europe/Prague", "Europe/Prague"),
 | 
			
		||||
                    ("Europe/Riga", "Europe/Riga"),
 | 
			
		||||
                    ("Europe/Rome", "Europe/Rome"),
 | 
			
		||||
                    ("Europe/Samara", "Europe/Samara"),
 | 
			
		||||
                    ("Europe/San_Marino", "Europe/San_Marino"),
 | 
			
		||||
                    ("Europe/Sarajevo", "Europe/Sarajevo"),
 | 
			
		||||
                    ("Europe/Saratov", "Europe/Saratov"),
 | 
			
		||||
                    ("Europe/Simferopol", "Europe/Simferopol"),
 | 
			
		||||
                    ("Europe/Skopje", "Europe/Skopje"),
 | 
			
		||||
                    ("Europe/Sofia", "Europe/Sofia"),
 | 
			
		||||
                    ("Europe/Stockholm", "Europe/Stockholm"),
 | 
			
		||||
                    ("Europe/Tallinn", "Europe/Tallinn"),
 | 
			
		||||
                    ("Europe/Tirane", "Europe/Tirane"),
 | 
			
		||||
                    ("Europe/Tiraspol", "Europe/Tiraspol"),
 | 
			
		||||
                    ("Europe/Ulyanovsk", "Europe/Ulyanovsk"),
 | 
			
		||||
                    ("Europe/Uzhgorod", "Europe/Uzhgorod"),
 | 
			
		||||
                    ("Europe/Vaduz", "Europe/Vaduz"),
 | 
			
		||||
                    ("Europe/Vatican", "Europe/Vatican"),
 | 
			
		||||
                    ("Europe/Vienna", "Europe/Vienna"),
 | 
			
		||||
                    ("Europe/Vilnius", "Europe/Vilnius"),
 | 
			
		||||
                    ("Europe/Volgograd", "Europe/Volgograd"),
 | 
			
		||||
                    ("Europe/Warsaw", "Europe/Warsaw"),
 | 
			
		||||
                    ("Europe/Zagreb", "Europe/Zagreb"),
 | 
			
		||||
                    ("Europe/Zaporozhye", "Europe/Zaporozhye"),
 | 
			
		||||
                    ("Europe/Zurich", "Europe/Zurich"),
 | 
			
		||||
                    ("GB", "GB"),
 | 
			
		||||
                    ("GB-Eire", "GB-Eire"),
 | 
			
		||||
                    ("GMT", "GMT"),
 | 
			
		||||
                    ("GMT+0", "GMT+0"),
 | 
			
		||||
                    ("GMT-0", "GMT-0"),
 | 
			
		||||
                    ("GMT0", "GMT0"),
 | 
			
		||||
                    ("Greenwich", "Greenwich"),
 | 
			
		||||
                    ("HST", "HST"),
 | 
			
		||||
                    ("Hongkong", "Hongkong"),
 | 
			
		||||
                    ("Iceland", "Iceland"),
 | 
			
		||||
                    ("Indian/Antananarivo", "Indian/Antananarivo"),
 | 
			
		||||
                    ("Indian/Chagos", "Indian/Chagos"),
 | 
			
		||||
                    ("Indian/Christmas", "Indian/Christmas"),
 | 
			
		||||
                    ("Indian/Cocos", "Indian/Cocos"),
 | 
			
		||||
                    ("Indian/Comoro", "Indian/Comoro"),
 | 
			
		||||
                    ("Indian/Kerguelen", "Indian/Kerguelen"),
 | 
			
		||||
                    ("Indian/Mahe", "Indian/Mahe"),
 | 
			
		||||
                    ("Indian/Maldives", "Indian/Maldives"),
 | 
			
		||||
                    ("Indian/Mauritius", "Indian/Mauritius"),
 | 
			
		||||
                    ("Indian/Mayotte", "Indian/Mayotte"),
 | 
			
		||||
                    ("Indian/Reunion", "Indian/Reunion"),
 | 
			
		||||
                    ("Iran", "Iran"),
 | 
			
		||||
                    ("Israel", "Israel"),
 | 
			
		||||
                    ("Jamaica", "Jamaica"),
 | 
			
		||||
                    ("Japan", "Japan"),
 | 
			
		||||
                    ("Kwajalein", "Kwajalein"),
 | 
			
		||||
                    ("Libya", "Libya"),
 | 
			
		||||
                    ("MET", "MET"),
 | 
			
		||||
                    ("MST", "MST"),
 | 
			
		||||
                    ("MST7MDT", "MST7MDT"),
 | 
			
		||||
                    ("Mexico/BajaNorte", "Mexico/BajaNorte"),
 | 
			
		||||
                    ("Mexico/BajaSur", "Mexico/BajaSur"),
 | 
			
		||||
                    ("Mexico/General", "Mexico/General"),
 | 
			
		||||
                    ("NZ", "NZ"),
 | 
			
		||||
                    ("NZ-CHAT", "NZ-CHAT"),
 | 
			
		||||
                    ("Navajo", "Navajo"),
 | 
			
		||||
                    ("PRC", "PRC"),
 | 
			
		||||
                    ("PST8PDT", "PST8PDT"),
 | 
			
		||||
                    ("Pacific/Apia", "Pacific/Apia"),
 | 
			
		||||
                    ("Pacific/Auckland", "Pacific/Auckland"),
 | 
			
		||||
                    ("Pacific/Bougainville", "Pacific/Bougainville"),
 | 
			
		||||
                    ("Pacific/Chatham", "Pacific/Chatham"),
 | 
			
		||||
                    ("Pacific/Chuuk", "Pacific/Chuuk"),
 | 
			
		||||
                    ("Pacific/Easter", "Pacific/Easter"),
 | 
			
		||||
                    ("Pacific/Efate", "Pacific/Efate"),
 | 
			
		||||
                    ("Pacific/Enderbury", "Pacific/Enderbury"),
 | 
			
		||||
                    ("Pacific/Fakaofo", "Pacific/Fakaofo"),
 | 
			
		||||
                    ("Pacific/Fiji", "Pacific/Fiji"),
 | 
			
		||||
                    ("Pacific/Funafuti", "Pacific/Funafuti"),
 | 
			
		||||
                    ("Pacific/Galapagos", "Pacific/Galapagos"),
 | 
			
		||||
                    ("Pacific/Gambier", "Pacific/Gambier"),
 | 
			
		||||
                    ("Pacific/Guadalcanal", "Pacific/Guadalcanal"),
 | 
			
		||||
                    ("Pacific/Guam", "Pacific/Guam"),
 | 
			
		||||
                    ("Pacific/Honolulu", "Pacific/Honolulu"),
 | 
			
		||||
                    ("Pacific/Johnston", "Pacific/Johnston"),
 | 
			
		||||
                    ("Pacific/Kanton", "Pacific/Kanton"),
 | 
			
		||||
                    ("Pacific/Kiritimati", "Pacific/Kiritimati"),
 | 
			
		||||
                    ("Pacific/Kosrae", "Pacific/Kosrae"),
 | 
			
		||||
                    ("Pacific/Kwajalein", "Pacific/Kwajalein"),
 | 
			
		||||
                    ("Pacific/Majuro", "Pacific/Majuro"),
 | 
			
		||||
                    ("Pacific/Marquesas", "Pacific/Marquesas"),
 | 
			
		||||
                    ("Pacific/Midway", "Pacific/Midway"),
 | 
			
		||||
                    ("Pacific/Nauru", "Pacific/Nauru"),
 | 
			
		||||
                    ("Pacific/Niue", "Pacific/Niue"),
 | 
			
		||||
                    ("Pacific/Norfolk", "Pacific/Norfolk"),
 | 
			
		||||
                    ("Pacific/Noumea", "Pacific/Noumea"),
 | 
			
		||||
                    ("Pacific/Pago_Pago", "Pacific/Pago_Pago"),
 | 
			
		||||
                    ("Pacific/Palau", "Pacific/Palau"),
 | 
			
		||||
                    ("Pacific/Pitcairn", "Pacific/Pitcairn"),
 | 
			
		||||
                    ("Pacific/Pohnpei", "Pacific/Pohnpei"),
 | 
			
		||||
                    ("Pacific/Ponape", "Pacific/Ponape"),
 | 
			
		||||
                    ("Pacific/Port_Moresby", "Pacific/Port_Moresby"),
 | 
			
		||||
                    ("Pacific/Rarotonga", "Pacific/Rarotonga"),
 | 
			
		||||
                    ("Pacific/Saipan", "Pacific/Saipan"),
 | 
			
		||||
                    ("Pacific/Samoa", "Pacific/Samoa"),
 | 
			
		||||
                    ("Pacific/Tahiti", "Pacific/Tahiti"),
 | 
			
		||||
                    ("Pacific/Tarawa", "Pacific/Tarawa"),
 | 
			
		||||
                    ("Pacific/Tongatapu", "Pacific/Tongatapu"),
 | 
			
		||||
                    ("Pacific/Truk", "Pacific/Truk"),
 | 
			
		||||
                    ("Pacific/Wake", "Pacific/Wake"),
 | 
			
		||||
                    ("Pacific/Wallis", "Pacific/Wallis"),
 | 
			
		||||
                    ("Pacific/Yap", "Pacific/Yap"),
 | 
			
		||||
                    ("Poland", "Poland"),
 | 
			
		||||
                    ("Portugal", "Portugal"),
 | 
			
		||||
                    ("ROC", "ROC"),
 | 
			
		||||
                    ("ROK", "ROK"),
 | 
			
		||||
                    ("Singapore", "Singapore"),
 | 
			
		||||
                    ("Turkey", "Turkey"),
 | 
			
		||||
                    ("UCT", "UCT"),
 | 
			
		||||
                    ("US/Alaska", "US/Alaska"),
 | 
			
		||||
                    ("US/Aleutian", "US/Aleutian"),
 | 
			
		||||
                    ("US/Arizona", "US/Arizona"),
 | 
			
		||||
                    ("US/Central", "US/Central"),
 | 
			
		||||
                    ("US/East-Indiana", "US/East-Indiana"),
 | 
			
		||||
                    ("US/Eastern", "US/Eastern"),
 | 
			
		||||
                    ("US/Hawaii", "US/Hawaii"),
 | 
			
		||||
                    ("US/Indiana-Starke", "US/Indiana-Starke"),
 | 
			
		||||
                    ("US/Michigan", "US/Michigan"),
 | 
			
		||||
                    ("US/Mountain", "US/Mountain"),
 | 
			
		||||
                    ("US/Pacific", "US/Pacific"),
 | 
			
		||||
                    ("US/Samoa", "US/Samoa"),
 | 
			
		||||
                    ("UTC", "UTC"),
 | 
			
		||||
                    ("Universal", "Universal"),
 | 
			
		||||
                    ("W-SU", "W-SU"),
 | 
			
		||||
                    ("WET", "WET"),
 | 
			
		||||
                    ("Zulu", "Zulu"),
 | 
			
		||||
                ],
 | 
			
		||||
                default=django.utils.timezone.get_current_timezone,
 | 
			
		||||
                help_text="timezone used for the date",
 | 
			
		||||
                max_length=100,
 | 
			
		||||
                verbose_name="timezone",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="program",
 | 
			
		||||
            field=models.ForeignKey(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                help_text="program related to it",
 | 
			
		||||
                on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                to="aircox.program",
 | 
			
		||||
                verbose_name="program",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="staticpage",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="station",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="stream",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="track",
 | 
			
		||||
            name="id",
 | 
			
		||||
            field=models.BigAutoField(
 | 
			
		||||
                auto_created=True,
 | 
			
		||||
                primary_key=True,
 | 
			
		||||
                serialize=False,
 | 
			
		||||
                verbose_name="ID",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										23
									
								
								aircox/migrations/0006_alter_sound_file.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								aircox/migrations/0006_alter_sound_file.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,23 @@
 | 
			
		||||
# Generated by Django 3.2.12 on 2022-03-26 15:21
 | 
			
		||||
 | 
			
		||||
import aircox.models.sound
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0005_auto_20220318_1205"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="file",
 | 
			
		||||
            field=models.FileField(
 | 
			
		||||
                db_index=True,
 | 
			
		||||
                max_length=256,
 | 
			
		||||
                upload_to=aircox.models.sound.Sound._upload_to,
 | 
			
		||||
                verbose_name="file",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@ -0,0 +1,710 @@
 | 
			
		||||
# Generated by Django 4.1 on 2022-10-06 13:47
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.utils.timezone
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0006_alter_sound_file"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="is_downloadable",
 | 
			
		||||
            field=models.BooleanField(
 | 
			
		||||
                default=False,
 | 
			
		||||
                help_text="whether it can be publicly downloaded by visitors (sound must be public)",
 | 
			
		||||
                verbose_name="downloadable",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="page",
 | 
			
		||||
            name="pub_date",
 | 
			
		||||
            field=models.DateTimeField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                db_index=True,
 | 
			
		||||
                null=True,
 | 
			
		||||
                verbose_name="publication date",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="timezone",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                choices=[
 | 
			
		||||
                    ("Africa/Abidjan", "Africa/Abidjan"),
 | 
			
		||||
                    ("Africa/Accra", "Africa/Accra"),
 | 
			
		||||
                    ("Africa/Addis_Ababa", "Africa/Addis_Ababa"),
 | 
			
		||||
                    ("Africa/Algiers", "Africa/Algiers"),
 | 
			
		||||
                    ("Africa/Asmara", "Africa/Asmara"),
 | 
			
		||||
                    ("Africa/Asmera", "Africa/Asmera"),
 | 
			
		||||
                    ("Africa/Bamako", "Africa/Bamako"),
 | 
			
		||||
                    ("Africa/Bangui", "Africa/Bangui"),
 | 
			
		||||
                    ("Africa/Banjul", "Africa/Banjul"),
 | 
			
		||||
                    ("Africa/Bissau", "Africa/Bissau"),
 | 
			
		||||
                    ("Africa/Blantyre", "Africa/Blantyre"),
 | 
			
		||||
                    ("Africa/Brazzaville", "Africa/Brazzaville"),
 | 
			
		||||
                    ("Africa/Bujumbura", "Africa/Bujumbura"),
 | 
			
		||||
                    ("Africa/Cairo", "Africa/Cairo"),
 | 
			
		||||
                    ("Africa/Casablanca", "Africa/Casablanca"),
 | 
			
		||||
                    ("Africa/Ceuta", "Africa/Ceuta"),
 | 
			
		||||
                    ("Africa/Conakry", "Africa/Conakry"),
 | 
			
		||||
                    ("Africa/Dakar", "Africa/Dakar"),
 | 
			
		||||
                    ("Africa/Dar_es_Salaam", "Africa/Dar_es_Salaam"),
 | 
			
		||||
                    ("Africa/Djibouti", "Africa/Djibouti"),
 | 
			
		||||
                    ("Africa/Douala", "Africa/Douala"),
 | 
			
		||||
                    ("Africa/El_Aaiun", "Africa/El_Aaiun"),
 | 
			
		||||
                    ("Africa/Freetown", "Africa/Freetown"),
 | 
			
		||||
                    ("Africa/Gaborone", "Africa/Gaborone"),
 | 
			
		||||
                    ("Africa/Harare", "Africa/Harare"),
 | 
			
		||||
                    ("Africa/Johannesburg", "Africa/Johannesburg"),
 | 
			
		||||
                    ("Africa/Juba", "Africa/Juba"),
 | 
			
		||||
                    ("Africa/Kampala", "Africa/Kampala"),
 | 
			
		||||
                    ("Africa/Khartoum", "Africa/Khartoum"),
 | 
			
		||||
                    ("Africa/Kigali", "Africa/Kigali"),
 | 
			
		||||
                    ("Africa/Kinshasa", "Africa/Kinshasa"),
 | 
			
		||||
                    ("Africa/Lagos", "Africa/Lagos"),
 | 
			
		||||
                    ("Africa/Libreville", "Africa/Libreville"),
 | 
			
		||||
                    ("Africa/Lome", "Africa/Lome"),
 | 
			
		||||
                    ("Africa/Luanda", "Africa/Luanda"),
 | 
			
		||||
                    ("Africa/Lubumbashi", "Africa/Lubumbashi"),
 | 
			
		||||
                    ("Africa/Lusaka", "Africa/Lusaka"),
 | 
			
		||||
                    ("Africa/Malabo", "Africa/Malabo"),
 | 
			
		||||
                    ("Africa/Maputo", "Africa/Maputo"),
 | 
			
		||||
                    ("Africa/Maseru", "Africa/Maseru"),
 | 
			
		||||
                    ("Africa/Mbabane", "Africa/Mbabane"),
 | 
			
		||||
                    ("Africa/Mogadishu", "Africa/Mogadishu"),
 | 
			
		||||
                    ("Africa/Monrovia", "Africa/Monrovia"),
 | 
			
		||||
                    ("Africa/Nairobi", "Africa/Nairobi"),
 | 
			
		||||
                    ("Africa/Ndjamena", "Africa/Ndjamena"),
 | 
			
		||||
                    ("Africa/Niamey", "Africa/Niamey"),
 | 
			
		||||
                    ("Africa/Nouakchott", "Africa/Nouakchott"),
 | 
			
		||||
                    ("Africa/Ouagadougou", "Africa/Ouagadougou"),
 | 
			
		||||
                    ("Africa/Porto-Novo", "Africa/Porto-Novo"),
 | 
			
		||||
                    ("Africa/Sao_Tome", "Africa/Sao_Tome"),
 | 
			
		||||
                    ("Africa/Timbuktu", "Africa/Timbuktu"),
 | 
			
		||||
                    ("Africa/Tripoli", "Africa/Tripoli"),
 | 
			
		||||
                    ("Africa/Tunis", "Africa/Tunis"),
 | 
			
		||||
                    ("Africa/Windhoek", "Africa/Windhoek"),
 | 
			
		||||
                    ("America/Adak", "America/Adak"),
 | 
			
		||||
                    ("America/Anchorage", "America/Anchorage"),
 | 
			
		||||
                    ("America/Anguilla", "America/Anguilla"),
 | 
			
		||||
                    ("America/Antigua", "America/Antigua"),
 | 
			
		||||
                    ("America/Araguaina", "America/Araguaina"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Buenos_Aires",
 | 
			
		||||
                        "America/Argentina/Buenos_Aires",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Catamarca",
 | 
			
		||||
                        "America/Argentina/Catamarca",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/ComodRivadavia",
 | 
			
		||||
                        "America/Argentina/ComodRivadavia",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Cordoba", "America/Argentina/Cordoba"),
 | 
			
		||||
                    ("America/Argentina/Jujuy", "America/Argentina/Jujuy"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/La_Rioja",
 | 
			
		||||
                        "America/Argentina/La_Rioja",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Mendoza", "America/Argentina/Mendoza"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/Rio_Gallegos",
 | 
			
		||||
                        "America/Argentina/Rio_Gallegos",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Salta", "America/Argentina/Salta"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/San_Juan",
 | 
			
		||||
                        "America/Argentina/San_Juan",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Argentina/San_Luis",
 | 
			
		||||
                        "America/Argentina/San_Luis",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Argentina/Tucuman", "America/Argentina/Tucuman"),
 | 
			
		||||
                    ("America/Argentina/Ushuaia", "America/Argentina/Ushuaia"),
 | 
			
		||||
                    ("America/Aruba", "America/Aruba"),
 | 
			
		||||
                    ("America/Asuncion", "America/Asuncion"),
 | 
			
		||||
                    ("America/Atikokan", "America/Atikokan"),
 | 
			
		||||
                    ("America/Atka", "America/Atka"),
 | 
			
		||||
                    ("America/Bahia", "America/Bahia"),
 | 
			
		||||
                    ("America/Bahia_Banderas", "America/Bahia_Banderas"),
 | 
			
		||||
                    ("America/Barbados", "America/Barbados"),
 | 
			
		||||
                    ("America/Belem", "America/Belem"),
 | 
			
		||||
                    ("America/Belize", "America/Belize"),
 | 
			
		||||
                    ("America/Blanc-Sablon", "America/Blanc-Sablon"),
 | 
			
		||||
                    ("America/Boa_Vista", "America/Boa_Vista"),
 | 
			
		||||
                    ("America/Bogota", "America/Bogota"),
 | 
			
		||||
                    ("America/Boise", "America/Boise"),
 | 
			
		||||
                    ("America/Buenos_Aires", "America/Buenos_Aires"),
 | 
			
		||||
                    ("America/Cambridge_Bay", "America/Cambridge_Bay"),
 | 
			
		||||
                    ("America/Campo_Grande", "America/Campo_Grande"),
 | 
			
		||||
                    ("America/Cancun", "America/Cancun"),
 | 
			
		||||
                    ("America/Caracas", "America/Caracas"),
 | 
			
		||||
                    ("America/Catamarca", "America/Catamarca"),
 | 
			
		||||
                    ("America/Cayenne", "America/Cayenne"),
 | 
			
		||||
                    ("America/Cayman", "America/Cayman"),
 | 
			
		||||
                    ("America/Chicago", "America/Chicago"),
 | 
			
		||||
                    ("America/Chihuahua", "America/Chihuahua"),
 | 
			
		||||
                    ("America/Coral_Harbour", "America/Coral_Harbour"),
 | 
			
		||||
                    ("America/Cordoba", "America/Cordoba"),
 | 
			
		||||
                    ("America/Costa_Rica", "America/Costa_Rica"),
 | 
			
		||||
                    ("America/Creston", "America/Creston"),
 | 
			
		||||
                    ("America/Cuiaba", "America/Cuiaba"),
 | 
			
		||||
                    ("America/Curacao", "America/Curacao"),
 | 
			
		||||
                    ("America/Danmarkshavn", "America/Danmarkshavn"),
 | 
			
		||||
                    ("America/Dawson", "America/Dawson"),
 | 
			
		||||
                    ("America/Dawson_Creek", "America/Dawson_Creek"),
 | 
			
		||||
                    ("America/Denver", "America/Denver"),
 | 
			
		||||
                    ("America/Detroit", "America/Detroit"),
 | 
			
		||||
                    ("America/Dominica", "America/Dominica"),
 | 
			
		||||
                    ("America/Edmonton", "America/Edmonton"),
 | 
			
		||||
                    ("America/Eirunepe", "America/Eirunepe"),
 | 
			
		||||
                    ("America/El_Salvador", "America/El_Salvador"),
 | 
			
		||||
                    ("America/Ensenada", "America/Ensenada"),
 | 
			
		||||
                    ("America/Fort_Nelson", "America/Fort_Nelson"),
 | 
			
		||||
                    ("America/Fort_Wayne", "America/Fort_Wayne"),
 | 
			
		||||
                    ("America/Fortaleza", "America/Fortaleza"),
 | 
			
		||||
                    ("America/Glace_Bay", "America/Glace_Bay"),
 | 
			
		||||
                    ("America/Godthab", "America/Godthab"),
 | 
			
		||||
                    ("America/Goose_Bay", "America/Goose_Bay"),
 | 
			
		||||
                    ("America/Grand_Turk", "America/Grand_Turk"),
 | 
			
		||||
                    ("America/Grenada", "America/Grenada"),
 | 
			
		||||
                    ("America/Guadeloupe", "America/Guadeloupe"),
 | 
			
		||||
                    ("America/Guatemala", "America/Guatemala"),
 | 
			
		||||
                    ("America/Guayaquil", "America/Guayaquil"),
 | 
			
		||||
                    ("America/Guyana", "America/Guyana"),
 | 
			
		||||
                    ("America/Halifax", "America/Halifax"),
 | 
			
		||||
                    ("America/Havana", "America/Havana"),
 | 
			
		||||
                    ("America/Hermosillo", "America/Hermosillo"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Indiana/Indianapolis",
 | 
			
		||||
                        "America/Indiana/Indianapolis",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Indiana/Knox", "America/Indiana/Knox"),
 | 
			
		||||
                    ("America/Indiana/Marengo", "America/Indiana/Marengo"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Indiana/Petersburg",
 | 
			
		||||
                        "America/Indiana/Petersburg",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Indiana/Tell_City", "America/Indiana/Tell_City"),
 | 
			
		||||
                    ("America/Indiana/Vevay", "America/Indiana/Vevay"),
 | 
			
		||||
                    ("America/Indiana/Vincennes", "America/Indiana/Vincennes"),
 | 
			
		||||
                    ("America/Indiana/Winamac", "America/Indiana/Winamac"),
 | 
			
		||||
                    ("America/Indianapolis", "America/Indianapolis"),
 | 
			
		||||
                    ("America/Inuvik", "America/Inuvik"),
 | 
			
		||||
                    ("America/Iqaluit", "America/Iqaluit"),
 | 
			
		||||
                    ("America/Jamaica", "America/Jamaica"),
 | 
			
		||||
                    ("America/Jujuy", "America/Jujuy"),
 | 
			
		||||
                    ("America/Juneau", "America/Juneau"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Kentucky/Louisville",
 | 
			
		||||
                        "America/Kentucky/Louisville",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/Kentucky/Monticello",
 | 
			
		||||
                        "America/Kentucky/Monticello",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Knox_IN", "America/Knox_IN"),
 | 
			
		||||
                    ("America/Kralendijk", "America/Kralendijk"),
 | 
			
		||||
                    ("America/La_Paz", "America/La_Paz"),
 | 
			
		||||
                    ("America/Lima", "America/Lima"),
 | 
			
		||||
                    ("America/Los_Angeles", "America/Los_Angeles"),
 | 
			
		||||
                    ("America/Louisville", "America/Louisville"),
 | 
			
		||||
                    ("America/Lower_Princes", "America/Lower_Princes"),
 | 
			
		||||
                    ("America/Maceio", "America/Maceio"),
 | 
			
		||||
                    ("America/Managua", "America/Managua"),
 | 
			
		||||
                    ("America/Manaus", "America/Manaus"),
 | 
			
		||||
                    ("America/Marigot", "America/Marigot"),
 | 
			
		||||
                    ("America/Martinique", "America/Martinique"),
 | 
			
		||||
                    ("America/Matamoros", "America/Matamoros"),
 | 
			
		||||
                    ("America/Mazatlan", "America/Mazatlan"),
 | 
			
		||||
                    ("America/Mendoza", "America/Mendoza"),
 | 
			
		||||
                    ("America/Menominee", "America/Menominee"),
 | 
			
		||||
                    ("America/Merida", "America/Merida"),
 | 
			
		||||
                    ("America/Metlakatla", "America/Metlakatla"),
 | 
			
		||||
                    ("America/Mexico_City", "America/Mexico_City"),
 | 
			
		||||
                    ("America/Miquelon", "America/Miquelon"),
 | 
			
		||||
                    ("America/Moncton", "America/Moncton"),
 | 
			
		||||
                    ("America/Monterrey", "America/Monterrey"),
 | 
			
		||||
                    ("America/Montevideo", "America/Montevideo"),
 | 
			
		||||
                    ("America/Montreal", "America/Montreal"),
 | 
			
		||||
                    ("America/Montserrat", "America/Montserrat"),
 | 
			
		||||
                    ("America/Nassau", "America/Nassau"),
 | 
			
		||||
                    ("America/New_York", "America/New_York"),
 | 
			
		||||
                    ("America/Nipigon", "America/Nipigon"),
 | 
			
		||||
                    ("America/Nome", "America/Nome"),
 | 
			
		||||
                    ("America/Noronha", "America/Noronha"),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/Beulah",
 | 
			
		||||
                        "America/North_Dakota/Beulah",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/Center",
 | 
			
		||||
                        "America/North_Dakota/Center",
 | 
			
		||||
                    ),
 | 
			
		||||
                    (
 | 
			
		||||
                        "America/North_Dakota/New_Salem",
 | 
			
		||||
                        "America/North_Dakota/New_Salem",
 | 
			
		||||
                    ),
 | 
			
		||||
                    ("America/Nuuk", "America/Nuuk"),
 | 
			
		||||
                    ("America/Ojinaga", "America/Ojinaga"),
 | 
			
		||||
                    ("America/Panama", "America/Panama"),
 | 
			
		||||
                    ("America/Pangnirtung", "America/Pangnirtung"),
 | 
			
		||||
                    ("America/Paramaribo", "America/Paramaribo"),
 | 
			
		||||
                    ("America/Phoenix", "America/Phoenix"),
 | 
			
		||||
                    ("America/Port-au-Prince", "America/Port-au-Prince"),
 | 
			
		||||
                    ("America/Port_of_Spain", "America/Port_of_Spain"),
 | 
			
		||||
                    ("America/Porto_Acre", "America/Porto_Acre"),
 | 
			
		||||
                    ("America/Porto_Velho", "America/Porto_Velho"),
 | 
			
		||||
                    ("America/Puerto_Rico", "America/Puerto_Rico"),
 | 
			
		||||
                    ("America/Punta_Arenas", "America/Punta_Arenas"),
 | 
			
		||||
                    ("America/Rainy_River", "America/Rainy_River"),
 | 
			
		||||
                    ("America/Rankin_Inlet", "America/Rankin_Inlet"),
 | 
			
		||||
                    ("America/Recife", "America/Recife"),
 | 
			
		||||
                    ("America/Regina", "America/Regina"),
 | 
			
		||||
                    ("America/Resolute", "America/Resolute"),
 | 
			
		||||
                    ("America/Rio_Branco", "America/Rio_Branco"),
 | 
			
		||||
                    ("America/Rosario", "America/Rosario"),
 | 
			
		||||
                    ("America/Santa_Isabel", "America/Santa_Isabel"),
 | 
			
		||||
                    ("America/Santarem", "America/Santarem"),
 | 
			
		||||
                    ("America/Santiago", "America/Santiago"),
 | 
			
		||||
                    ("America/Santo_Domingo", "America/Santo_Domingo"),
 | 
			
		||||
                    ("America/Sao_Paulo", "America/Sao_Paulo"),
 | 
			
		||||
                    ("America/Scoresbysund", "America/Scoresbysund"),
 | 
			
		||||
                    ("America/Shiprock", "America/Shiprock"),
 | 
			
		||||
                    ("America/Sitka", "America/Sitka"),
 | 
			
		||||
                    ("America/St_Barthelemy", "America/St_Barthelemy"),
 | 
			
		||||
                    ("America/St_Johns", "America/St_Johns"),
 | 
			
		||||
                    ("America/St_Kitts", "America/St_Kitts"),
 | 
			
		||||
                    ("America/St_Lucia", "America/St_Lucia"),
 | 
			
		||||
                    ("America/St_Thomas", "America/St_Thomas"),
 | 
			
		||||
                    ("America/St_Vincent", "America/St_Vincent"),
 | 
			
		||||
                    ("America/Swift_Current", "America/Swift_Current"),
 | 
			
		||||
                    ("America/Tegucigalpa", "America/Tegucigalpa"),
 | 
			
		||||
                    ("America/Thule", "America/Thule"),
 | 
			
		||||
                    ("America/Thunder_Bay", "America/Thunder_Bay"),
 | 
			
		||||
                    ("America/Tijuana", "America/Tijuana"),
 | 
			
		||||
                    ("America/Toronto", "America/Toronto"),
 | 
			
		||||
                    ("America/Tortola", "America/Tortola"),
 | 
			
		||||
                    ("America/Vancouver", "America/Vancouver"),
 | 
			
		||||
                    ("America/Virgin", "America/Virgin"),
 | 
			
		||||
                    ("America/Whitehorse", "America/Whitehorse"),
 | 
			
		||||
                    ("America/Winnipeg", "America/Winnipeg"),
 | 
			
		||||
                    ("America/Yakutat", "America/Yakutat"),
 | 
			
		||||
                    ("America/Yellowknife", "America/Yellowknife"),
 | 
			
		||||
                    ("Antarctica/Casey", "Antarctica/Casey"),
 | 
			
		||||
                    ("Antarctica/Davis", "Antarctica/Davis"),
 | 
			
		||||
                    ("Antarctica/DumontDUrville", "Antarctica/DumontDUrville"),
 | 
			
		||||
                    ("Antarctica/Macquarie", "Antarctica/Macquarie"),
 | 
			
		||||
                    ("Antarctica/Mawson", "Antarctica/Mawson"),
 | 
			
		||||
                    ("Antarctica/McMurdo", "Antarctica/McMurdo"),
 | 
			
		||||
                    ("Antarctica/Palmer", "Antarctica/Palmer"),
 | 
			
		||||
                    ("Antarctica/Rothera", "Antarctica/Rothera"),
 | 
			
		||||
                    ("Antarctica/South_Pole", "Antarctica/South_Pole"),
 | 
			
		||||
                    ("Antarctica/Syowa", "Antarctica/Syowa"),
 | 
			
		||||
                    ("Antarctica/Troll", "Antarctica/Troll"),
 | 
			
		||||
                    ("Antarctica/Vostok", "Antarctica/Vostok"),
 | 
			
		||||
                    ("Arctic/Longyearbyen", "Arctic/Longyearbyen"),
 | 
			
		||||
                    ("Asia/Aden", "Asia/Aden"),
 | 
			
		||||
                    ("Asia/Almaty", "Asia/Almaty"),
 | 
			
		||||
                    ("Asia/Amman", "Asia/Amman"),
 | 
			
		||||
                    ("Asia/Anadyr", "Asia/Anadyr"),
 | 
			
		||||
                    ("Asia/Aqtau", "Asia/Aqtau"),
 | 
			
		||||
                    ("Asia/Aqtobe", "Asia/Aqtobe"),
 | 
			
		||||
                    ("Asia/Ashgabat", "Asia/Ashgabat"),
 | 
			
		||||
                    ("Asia/Ashkhabad", "Asia/Ashkhabad"),
 | 
			
		||||
                    ("Asia/Atyrau", "Asia/Atyrau"),
 | 
			
		||||
                    ("Asia/Baghdad", "Asia/Baghdad"),
 | 
			
		||||
                    ("Asia/Bahrain", "Asia/Bahrain"),
 | 
			
		||||
                    ("Asia/Baku", "Asia/Baku"),
 | 
			
		||||
                    ("Asia/Bangkok", "Asia/Bangkok"),
 | 
			
		||||
                    ("Asia/Barnaul", "Asia/Barnaul"),
 | 
			
		||||
                    ("Asia/Beirut", "Asia/Beirut"),
 | 
			
		||||
                    ("Asia/Bishkek", "Asia/Bishkek"),
 | 
			
		||||
                    ("Asia/Brunei", "Asia/Brunei"),
 | 
			
		||||
                    ("Asia/Calcutta", "Asia/Calcutta"),
 | 
			
		||||
                    ("Asia/Chita", "Asia/Chita"),
 | 
			
		||||
                    ("Asia/Choibalsan", "Asia/Choibalsan"),
 | 
			
		||||
                    ("Asia/Chongqing", "Asia/Chongqing"),
 | 
			
		||||
                    ("Asia/Chungking", "Asia/Chungking"),
 | 
			
		||||
                    ("Asia/Colombo", "Asia/Colombo"),
 | 
			
		||||
                    ("Asia/Dacca", "Asia/Dacca"),
 | 
			
		||||
                    ("Asia/Damascus", "Asia/Damascus"),
 | 
			
		||||
                    ("Asia/Dhaka", "Asia/Dhaka"),
 | 
			
		||||
                    ("Asia/Dili", "Asia/Dili"),
 | 
			
		||||
                    ("Asia/Dubai", "Asia/Dubai"),
 | 
			
		||||
                    ("Asia/Dushanbe", "Asia/Dushanbe"),
 | 
			
		||||
                    ("Asia/Famagusta", "Asia/Famagusta"),
 | 
			
		||||
                    ("Asia/Gaza", "Asia/Gaza"),
 | 
			
		||||
                    ("Asia/Harbin", "Asia/Harbin"),
 | 
			
		||||
                    ("Asia/Hebron", "Asia/Hebron"),
 | 
			
		||||
                    ("Asia/Ho_Chi_Minh", "Asia/Ho_Chi_Minh"),
 | 
			
		||||
                    ("Asia/Hong_Kong", "Asia/Hong_Kong"),
 | 
			
		||||
                    ("Asia/Hovd", "Asia/Hovd"),
 | 
			
		||||
                    ("Asia/Irkutsk", "Asia/Irkutsk"),
 | 
			
		||||
                    ("Asia/Istanbul", "Asia/Istanbul"),
 | 
			
		||||
                    ("Asia/Jakarta", "Asia/Jakarta"),
 | 
			
		||||
                    ("Asia/Jayapura", "Asia/Jayapura"),
 | 
			
		||||
                    ("Asia/Jerusalem", "Asia/Jerusalem"),
 | 
			
		||||
                    ("Asia/Kabul", "Asia/Kabul"),
 | 
			
		||||
                    ("Asia/Kamchatka", "Asia/Kamchatka"),
 | 
			
		||||
                    ("Asia/Karachi", "Asia/Karachi"),
 | 
			
		||||
                    ("Asia/Kashgar", "Asia/Kashgar"),
 | 
			
		||||
                    ("Asia/Kathmandu", "Asia/Kathmandu"),
 | 
			
		||||
                    ("Asia/Katmandu", "Asia/Katmandu"),
 | 
			
		||||
                    ("Asia/Khandyga", "Asia/Khandyga"),
 | 
			
		||||
                    ("Asia/Kolkata", "Asia/Kolkata"),
 | 
			
		||||
                    ("Asia/Krasnoyarsk", "Asia/Krasnoyarsk"),
 | 
			
		||||
                    ("Asia/Kuala_Lumpur", "Asia/Kuala_Lumpur"),
 | 
			
		||||
                    ("Asia/Kuching", "Asia/Kuching"),
 | 
			
		||||
                    ("Asia/Kuwait", "Asia/Kuwait"),
 | 
			
		||||
                    ("Asia/Macao", "Asia/Macao"),
 | 
			
		||||
                    ("Asia/Macau", "Asia/Macau"),
 | 
			
		||||
                    ("Asia/Magadan", "Asia/Magadan"),
 | 
			
		||||
                    ("Asia/Makassar", "Asia/Makassar"),
 | 
			
		||||
                    ("Asia/Manila", "Asia/Manila"),
 | 
			
		||||
                    ("Asia/Muscat", "Asia/Muscat"),
 | 
			
		||||
                    ("Asia/Nicosia", "Asia/Nicosia"),
 | 
			
		||||
                    ("Asia/Novokuznetsk", "Asia/Novokuznetsk"),
 | 
			
		||||
                    ("Asia/Novosibirsk", "Asia/Novosibirsk"),
 | 
			
		||||
                    ("Asia/Omsk", "Asia/Omsk"),
 | 
			
		||||
                    ("Asia/Oral", "Asia/Oral"),
 | 
			
		||||
                    ("Asia/Phnom_Penh", "Asia/Phnom_Penh"),
 | 
			
		||||
                    ("Asia/Pontianak", "Asia/Pontianak"),
 | 
			
		||||
                    ("Asia/Pyongyang", "Asia/Pyongyang"),
 | 
			
		||||
                    ("Asia/Qatar", "Asia/Qatar"),
 | 
			
		||||
                    ("Asia/Qostanay", "Asia/Qostanay"),
 | 
			
		||||
                    ("Asia/Qyzylorda", "Asia/Qyzylorda"),
 | 
			
		||||
                    ("Asia/Rangoon", "Asia/Rangoon"),
 | 
			
		||||
                    ("Asia/Riyadh", "Asia/Riyadh"),
 | 
			
		||||
                    ("Asia/Saigon", "Asia/Saigon"),
 | 
			
		||||
                    ("Asia/Sakhalin", "Asia/Sakhalin"),
 | 
			
		||||
                    ("Asia/Samarkand", "Asia/Samarkand"),
 | 
			
		||||
                    ("Asia/Seoul", "Asia/Seoul"),
 | 
			
		||||
                    ("Asia/Shanghai", "Asia/Shanghai"),
 | 
			
		||||
                    ("Asia/Singapore", "Asia/Singapore"),
 | 
			
		||||
                    ("Asia/Srednekolymsk", "Asia/Srednekolymsk"),
 | 
			
		||||
                    ("Asia/Taipei", "Asia/Taipei"),
 | 
			
		||||
                    ("Asia/Tashkent", "Asia/Tashkent"),
 | 
			
		||||
                    ("Asia/Tbilisi", "Asia/Tbilisi"),
 | 
			
		||||
                    ("Asia/Tehran", "Asia/Tehran"),
 | 
			
		||||
                    ("Asia/Tel_Aviv", "Asia/Tel_Aviv"),
 | 
			
		||||
                    ("Asia/Thimbu", "Asia/Thimbu"),
 | 
			
		||||
                    ("Asia/Thimphu", "Asia/Thimphu"),
 | 
			
		||||
                    ("Asia/Tokyo", "Asia/Tokyo"),
 | 
			
		||||
                    ("Asia/Tomsk", "Asia/Tomsk"),
 | 
			
		||||
                    ("Asia/Ujung_Pandang", "Asia/Ujung_Pandang"),
 | 
			
		||||
                    ("Asia/Ulaanbaatar", "Asia/Ulaanbaatar"),
 | 
			
		||||
                    ("Asia/Ulan_Bator", "Asia/Ulan_Bator"),
 | 
			
		||||
                    ("Asia/Urumqi", "Asia/Urumqi"),
 | 
			
		||||
                    ("Asia/Ust-Nera", "Asia/Ust-Nera"),
 | 
			
		||||
                    ("Asia/Vientiane", "Asia/Vientiane"),
 | 
			
		||||
                    ("Asia/Vladivostok", "Asia/Vladivostok"),
 | 
			
		||||
                    ("Asia/Yakutsk", "Asia/Yakutsk"),
 | 
			
		||||
                    ("Asia/Yangon", "Asia/Yangon"),
 | 
			
		||||
                    ("Asia/Yekaterinburg", "Asia/Yekaterinburg"),
 | 
			
		||||
                    ("Asia/Yerevan", "Asia/Yerevan"),
 | 
			
		||||
                    ("Atlantic/Azores", "Atlantic/Azores"),
 | 
			
		||||
                    ("Atlantic/Bermuda", "Atlantic/Bermuda"),
 | 
			
		||||
                    ("Atlantic/Canary", "Atlantic/Canary"),
 | 
			
		||||
                    ("Atlantic/Cape_Verde", "Atlantic/Cape_Verde"),
 | 
			
		||||
                    ("Atlantic/Faeroe", "Atlantic/Faeroe"),
 | 
			
		||||
                    ("Atlantic/Faroe", "Atlantic/Faroe"),
 | 
			
		||||
                    ("Atlantic/Jan_Mayen", "Atlantic/Jan_Mayen"),
 | 
			
		||||
                    ("Atlantic/Madeira", "Atlantic/Madeira"),
 | 
			
		||||
                    ("Atlantic/Reykjavik", "Atlantic/Reykjavik"),
 | 
			
		||||
                    ("Atlantic/South_Georgia", "Atlantic/South_Georgia"),
 | 
			
		||||
                    ("Atlantic/St_Helena", "Atlantic/St_Helena"),
 | 
			
		||||
                    ("Atlantic/Stanley", "Atlantic/Stanley"),
 | 
			
		||||
                    ("Australia/ACT", "Australia/ACT"),
 | 
			
		||||
                    ("Australia/Adelaide", "Australia/Adelaide"),
 | 
			
		||||
                    ("Australia/Brisbane", "Australia/Brisbane"),
 | 
			
		||||
                    ("Australia/Broken_Hill", "Australia/Broken_Hill"),
 | 
			
		||||
                    ("Australia/Canberra", "Australia/Canberra"),
 | 
			
		||||
                    ("Australia/Currie", "Australia/Currie"),
 | 
			
		||||
                    ("Australia/Darwin", "Australia/Darwin"),
 | 
			
		||||
                    ("Australia/Eucla", "Australia/Eucla"),
 | 
			
		||||
                    ("Australia/Hobart", "Australia/Hobart"),
 | 
			
		||||
                    ("Australia/LHI", "Australia/LHI"),
 | 
			
		||||
                    ("Australia/Lindeman", "Australia/Lindeman"),
 | 
			
		||||
                    ("Australia/Lord_Howe", "Australia/Lord_Howe"),
 | 
			
		||||
                    ("Australia/Melbourne", "Australia/Melbourne"),
 | 
			
		||||
                    ("Australia/NSW", "Australia/NSW"),
 | 
			
		||||
                    ("Australia/North", "Australia/North"),
 | 
			
		||||
                    ("Australia/Perth", "Australia/Perth"),
 | 
			
		||||
                    ("Australia/Queensland", "Australia/Queensland"),
 | 
			
		||||
                    ("Australia/South", "Australia/South"),
 | 
			
		||||
                    ("Australia/Sydney", "Australia/Sydney"),
 | 
			
		||||
                    ("Australia/Tasmania", "Australia/Tasmania"),
 | 
			
		||||
                    ("Australia/Victoria", "Australia/Victoria"),
 | 
			
		||||
                    ("Australia/West", "Australia/West"),
 | 
			
		||||
                    ("Australia/Yancowinna", "Australia/Yancowinna"),
 | 
			
		||||
                    ("Brazil/Acre", "Brazil/Acre"),
 | 
			
		||||
                    ("Brazil/DeNoronha", "Brazil/DeNoronha"),
 | 
			
		||||
                    ("Brazil/East", "Brazil/East"),
 | 
			
		||||
                    ("Brazil/West", "Brazil/West"),
 | 
			
		||||
                    ("CET", "CET"),
 | 
			
		||||
                    ("CST6CDT", "CST6CDT"),
 | 
			
		||||
                    ("Canada/Atlantic", "Canada/Atlantic"),
 | 
			
		||||
                    ("Canada/Central", "Canada/Central"),
 | 
			
		||||
                    ("Canada/Eastern", "Canada/Eastern"),
 | 
			
		||||
                    ("Canada/Mountain", "Canada/Mountain"),
 | 
			
		||||
                    ("Canada/Newfoundland", "Canada/Newfoundland"),
 | 
			
		||||
                    ("Canada/Pacific", "Canada/Pacific"),
 | 
			
		||||
                    ("Canada/Saskatchewan", "Canada/Saskatchewan"),
 | 
			
		||||
                    ("Canada/Yukon", "Canada/Yukon"),
 | 
			
		||||
                    ("Chile/Continental", "Chile/Continental"),
 | 
			
		||||
                    ("Chile/EasterIsland", "Chile/EasterIsland"),
 | 
			
		||||
                    ("Cuba", "Cuba"),
 | 
			
		||||
                    ("EET", "EET"),
 | 
			
		||||
                    ("EST", "EST"),
 | 
			
		||||
                    ("EST5EDT", "EST5EDT"),
 | 
			
		||||
                    ("Egypt", "Egypt"),
 | 
			
		||||
                    ("Eire", "Eire"),
 | 
			
		||||
                    ("Etc/GMT", "Etc/GMT"),
 | 
			
		||||
                    ("Etc/GMT+0", "Etc/GMT+0"),
 | 
			
		||||
                    ("Etc/GMT+1", "Etc/GMT+1"),
 | 
			
		||||
                    ("Etc/GMT+10", "Etc/GMT+10"),
 | 
			
		||||
                    ("Etc/GMT+11", "Etc/GMT+11"),
 | 
			
		||||
                    ("Etc/GMT+12", "Etc/GMT+12"),
 | 
			
		||||
                    ("Etc/GMT+2", "Etc/GMT+2"),
 | 
			
		||||
                    ("Etc/GMT+3", "Etc/GMT+3"),
 | 
			
		||||
                    ("Etc/GMT+4", "Etc/GMT+4"),
 | 
			
		||||
                    ("Etc/GMT+5", "Etc/GMT+5"),
 | 
			
		||||
                    ("Etc/GMT+6", "Etc/GMT+6"),
 | 
			
		||||
                    ("Etc/GMT+7", "Etc/GMT+7"),
 | 
			
		||||
                    ("Etc/GMT+8", "Etc/GMT+8"),
 | 
			
		||||
                    ("Etc/GMT+9", "Etc/GMT+9"),
 | 
			
		||||
                    ("Etc/GMT-0", "Etc/GMT-0"),
 | 
			
		||||
                    ("Etc/GMT-1", "Etc/GMT-1"),
 | 
			
		||||
                    ("Etc/GMT-10", "Etc/GMT-10"),
 | 
			
		||||
                    ("Etc/GMT-11", "Etc/GMT-11"),
 | 
			
		||||
                    ("Etc/GMT-12", "Etc/GMT-12"),
 | 
			
		||||
                    ("Etc/GMT-13", "Etc/GMT-13"),
 | 
			
		||||
                    ("Etc/GMT-14", "Etc/GMT-14"),
 | 
			
		||||
                    ("Etc/GMT-2", "Etc/GMT-2"),
 | 
			
		||||
                    ("Etc/GMT-3", "Etc/GMT-3"),
 | 
			
		||||
                    ("Etc/GMT-4", "Etc/GMT-4"),
 | 
			
		||||
                    ("Etc/GMT-5", "Etc/GMT-5"),
 | 
			
		||||
                    ("Etc/GMT-6", "Etc/GMT-6"),
 | 
			
		||||
                    ("Etc/GMT-7", "Etc/GMT-7"),
 | 
			
		||||
                    ("Etc/GMT-8", "Etc/GMT-8"),
 | 
			
		||||
                    ("Etc/GMT-9", "Etc/GMT-9"),
 | 
			
		||||
                    ("Etc/GMT0", "Etc/GMT0"),
 | 
			
		||||
                    ("Etc/Greenwich", "Etc/Greenwich"),
 | 
			
		||||
                    ("Etc/UCT", "Etc/UCT"),
 | 
			
		||||
                    ("Etc/UTC", "Etc/UTC"),
 | 
			
		||||
                    ("Etc/Universal", "Etc/Universal"),
 | 
			
		||||
                    ("Etc/Zulu", "Etc/Zulu"),
 | 
			
		||||
                    ("Europe/Amsterdam", "Europe/Amsterdam"),
 | 
			
		||||
                    ("Europe/Andorra", "Europe/Andorra"),
 | 
			
		||||
                    ("Europe/Astrakhan", "Europe/Astrakhan"),
 | 
			
		||||
                    ("Europe/Athens", "Europe/Athens"),
 | 
			
		||||
                    ("Europe/Belfast", "Europe/Belfast"),
 | 
			
		||||
                    ("Europe/Belgrade", "Europe/Belgrade"),
 | 
			
		||||
                    ("Europe/Berlin", "Europe/Berlin"),
 | 
			
		||||
                    ("Europe/Bratislava", "Europe/Bratislava"),
 | 
			
		||||
                    ("Europe/Brussels", "Europe/Brussels"),
 | 
			
		||||
                    ("Europe/Bucharest", "Europe/Bucharest"),
 | 
			
		||||
                    ("Europe/Budapest", "Europe/Budapest"),
 | 
			
		||||
                    ("Europe/Busingen", "Europe/Busingen"),
 | 
			
		||||
                    ("Europe/Chisinau", "Europe/Chisinau"),
 | 
			
		||||
                    ("Europe/Copenhagen", "Europe/Copenhagen"),
 | 
			
		||||
                    ("Europe/Dublin", "Europe/Dublin"),
 | 
			
		||||
                    ("Europe/Gibraltar", "Europe/Gibraltar"),
 | 
			
		||||
                    ("Europe/Guernsey", "Europe/Guernsey"),
 | 
			
		||||
                    ("Europe/Helsinki", "Europe/Helsinki"),
 | 
			
		||||
                    ("Europe/Isle_of_Man", "Europe/Isle_of_Man"),
 | 
			
		||||
                    ("Europe/Istanbul", "Europe/Istanbul"),
 | 
			
		||||
                    ("Europe/Jersey", "Europe/Jersey"),
 | 
			
		||||
                    ("Europe/Kaliningrad", "Europe/Kaliningrad"),
 | 
			
		||||
                    ("Europe/Kiev", "Europe/Kiev"),
 | 
			
		||||
                    ("Europe/Kirov", "Europe/Kirov"),
 | 
			
		||||
                    ("Europe/Kyiv", "Europe/Kyiv"),
 | 
			
		||||
                    ("Europe/Lisbon", "Europe/Lisbon"),
 | 
			
		||||
                    ("Europe/Ljubljana", "Europe/Ljubljana"),
 | 
			
		||||
                    ("Europe/London", "Europe/London"),
 | 
			
		||||
                    ("Europe/Luxembourg", "Europe/Luxembourg"),
 | 
			
		||||
                    ("Europe/Madrid", "Europe/Madrid"),
 | 
			
		||||
                    ("Europe/Malta", "Europe/Malta"),
 | 
			
		||||
                    ("Europe/Mariehamn", "Europe/Mariehamn"),
 | 
			
		||||
                    ("Europe/Minsk", "Europe/Minsk"),
 | 
			
		||||
                    ("Europe/Monaco", "Europe/Monaco"),
 | 
			
		||||
                    ("Europe/Moscow", "Europe/Moscow"),
 | 
			
		||||
                    ("Europe/Nicosia", "Europe/Nicosia"),
 | 
			
		||||
                    ("Europe/Oslo", "Europe/Oslo"),
 | 
			
		||||
                    ("Europe/Paris", "Europe/Paris"),
 | 
			
		||||
                    ("Europe/Podgorica", "Europe/Podgorica"),
 | 
			
		||||
                    ("Europe/Prague", "Europe/Prague"),
 | 
			
		||||
                    ("Europe/Riga", "Europe/Riga"),
 | 
			
		||||
                    ("Europe/Rome", "Europe/Rome"),
 | 
			
		||||
                    ("Europe/Samara", "Europe/Samara"),
 | 
			
		||||
                    ("Europe/San_Marino", "Europe/San_Marino"),
 | 
			
		||||
                    ("Europe/Sarajevo", "Europe/Sarajevo"),
 | 
			
		||||
                    ("Europe/Saratov", "Europe/Saratov"),
 | 
			
		||||
                    ("Europe/Simferopol", "Europe/Simferopol"),
 | 
			
		||||
                    ("Europe/Skopje", "Europe/Skopje"),
 | 
			
		||||
                    ("Europe/Sofia", "Europe/Sofia"),
 | 
			
		||||
                    ("Europe/Stockholm", "Europe/Stockholm"),
 | 
			
		||||
                    ("Europe/Tallinn", "Europe/Tallinn"),
 | 
			
		||||
                    ("Europe/Tirane", "Europe/Tirane"),
 | 
			
		||||
                    ("Europe/Tiraspol", "Europe/Tiraspol"),
 | 
			
		||||
                    ("Europe/Ulyanovsk", "Europe/Ulyanovsk"),
 | 
			
		||||
                    ("Europe/Uzhgorod", "Europe/Uzhgorod"),
 | 
			
		||||
                    ("Europe/Vaduz", "Europe/Vaduz"),
 | 
			
		||||
                    ("Europe/Vatican", "Europe/Vatican"),
 | 
			
		||||
                    ("Europe/Vienna", "Europe/Vienna"),
 | 
			
		||||
                    ("Europe/Vilnius", "Europe/Vilnius"),
 | 
			
		||||
                    ("Europe/Volgograd", "Europe/Volgograd"),
 | 
			
		||||
                    ("Europe/Warsaw", "Europe/Warsaw"),
 | 
			
		||||
                    ("Europe/Zagreb", "Europe/Zagreb"),
 | 
			
		||||
                    ("Europe/Zaporozhye", "Europe/Zaporozhye"),
 | 
			
		||||
                    ("Europe/Zurich", "Europe/Zurich"),
 | 
			
		||||
                    ("GB", "GB"),
 | 
			
		||||
                    ("GB-Eire", "GB-Eire"),
 | 
			
		||||
                    ("GMT", "GMT"),
 | 
			
		||||
                    ("GMT+0", "GMT+0"),
 | 
			
		||||
                    ("GMT-0", "GMT-0"),
 | 
			
		||||
                    ("GMT0", "GMT0"),
 | 
			
		||||
                    ("Greenwich", "Greenwich"),
 | 
			
		||||
                    ("HST", "HST"),
 | 
			
		||||
                    ("Hongkong", "Hongkong"),
 | 
			
		||||
                    ("Iceland", "Iceland"),
 | 
			
		||||
                    ("Indian/Antananarivo", "Indian/Antananarivo"),
 | 
			
		||||
                    ("Indian/Chagos", "Indian/Chagos"),
 | 
			
		||||
                    ("Indian/Christmas", "Indian/Christmas"),
 | 
			
		||||
                    ("Indian/Cocos", "Indian/Cocos"),
 | 
			
		||||
                    ("Indian/Comoro", "Indian/Comoro"),
 | 
			
		||||
                    ("Indian/Kerguelen", "Indian/Kerguelen"),
 | 
			
		||||
                    ("Indian/Mahe", "Indian/Mahe"),
 | 
			
		||||
                    ("Indian/Maldives", "Indian/Maldives"),
 | 
			
		||||
                    ("Indian/Mauritius", "Indian/Mauritius"),
 | 
			
		||||
                    ("Indian/Mayotte", "Indian/Mayotte"),
 | 
			
		||||
                    ("Indian/Reunion", "Indian/Reunion"),
 | 
			
		||||
                    ("Iran", "Iran"),
 | 
			
		||||
                    ("Israel", "Israel"),
 | 
			
		||||
                    ("Jamaica", "Jamaica"),
 | 
			
		||||
                    ("Japan", "Japan"),
 | 
			
		||||
                    ("Kwajalein", "Kwajalein"),
 | 
			
		||||
                    ("Libya", "Libya"),
 | 
			
		||||
                    ("MET", "MET"),
 | 
			
		||||
                    ("MST", "MST"),
 | 
			
		||||
                    ("MST7MDT", "MST7MDT"),
 | 
			
		||||
                    ("Mexico/BajaNorte", "Mexico/BajaNorte"),
 | 
			
		||||
                    ("Mexico/BajaSur", "Mexico/BajaSur"),
 | 
			
		||||
                    ("Mexico/General", "Mexico/General"),
 | 
			
		||||
                    ("NZ", "NZ"),
 | 
			
		||||
                    ("NZ-CHAT", "NZ-CHAT"),
 | 
			
		||||
                    ("Navajo", "Navajo"),
 | 
			
		||||
                    ("PRC", "PRC"),
 | 
			
		||||
                    ("PST8PDT", "PST8PDT"),
 | 
			
		||||
                    ("Pacific/Apia", "Pacific/Apia"),
 | 
			
		||||
                    ("Pacific/Auckland", "Pacific/Auckland"),
 | 
			
		||||
                    ("Pacific/Bougainville", "Pacific/Bougainville"),
 | 
			
		||||
                    ("Pacific/Chatham", "Pacific/Chatham"),
 | 
			
		||||
                    ("Pacific/Chuuk", "Pacific/Chuuk"),
 | 
			
		||||
                    ("Pacific/Easter", "Pacific/Easter"),
 | 
			
		||||
                    ("Pacific/Efate", "Pacific/Efate"),
 | 
			
		||||
                    ("Pacific/Enderbury", "Pacific/Enderbury"),
 | 
			
		||||
                    ("Pacific/Fakaofo", "Pacific/Fakaofo"),
 | 
			
		||||
                    ("Pacific/Fiji", "Pacific/Fiji"),
 | 
			
		||||
                    ("Pacific/Funafuti", "Pacific/Funafuti"),
 | 
			
		||||
                    ("Pacific/Galapagos", "Pacific/Galapagos"),
 | 
			
		||||
                    ("Pacific/Gambier", "Pacific/Gambier"),
 | 
			
		||||
                    ("Pacific/Guadalcanal", "Pacific/Guadalcanal"),
 | 
			
		||||
                    ("Pacific/Guam", "Pacific/Guam"),
 | 
			
		||||
                    ("Pacific/Honolulu", "Pacific/Honolulu"),
 | 
			
		||||
                    ("Pacific/Johnston", "Pacific/Johnston"),
 | 
			
		||||
                    ("Pacific/Kanton", "Pacific/Kanton"),
 | 
			
		||||
                    ("Pacific/Kiritimati", "Pacific/Kiritimati"),
 | 
			
		||||
                    ("Pacific/Kosrae", "Pacific/Kosrae"),
 | 
			
		||||
                    ("Pacific/Kwajalein", "Pacific/Kwajalein"),
 | 
			
		||||
                    ("Pacific/Majuro", "Pacific/Majuro"),
 | 
			
		||||
                    ("Pacific/Marquesas", "Pacific/Marquesas"),
 | 
			
		||||
                    ("Pacific/Midway", "Pacific/Midway"),
 | 
			
		||||
                    ("Pacific/Nauru", "Pacific/Nauru"),
 | 
			
		||||
                    ("Pacific/Niue", "Pacific/Niue"),
 | 
			
		||||
                    ("Pacific/Norfolk", "Pacific/Norfolk"),
 | 
			
		||||
                    ("Pacific/Noumea", "Pacific/Noumea"),
 | 
			
		||||
                    ("Pacific/Pago_Pago", "Pacific/Pago_Pago"),
 | 
			
		||||
                    ("Pacific/Palau", "Pacific/Palau"),
 | 
			
		||||
                    ("Pacific/Pitcairn", "Pacific/Pitcairn"),
 | 
			
		||||
                    ("Pacific/Pohnpei", "Pacific/Pohnpei"),
 | 
			
		||||
                    ("Pacific/Ponape", "Pacific/Ponape"),
 | 
			
		||||
                    ("Pacific/Port_Moresby", "Pacific/Port_Moresby"),
 | 
			
		||||
                    ("Pacific/Rarotonga", "Pacific/Rarotonga"),
 | 
			
		||||
                    ("Pacific/Saipan", "Pacific/Saipan"),
 | 
			
		||||
                    ("Pacific/Samoa", "Pacific/Samoa"),
 | 
			
		||||
                    ("Pacific/Tahiti", "Pacific/Tahiti"),
 | 
			
		||||
                    ("Pacific/Tarawa", "Pacific/Tarawa"),
 | 
			
		||||
                    ("Pacific/Tongatapu", "Pacific/Tongatapu"),
 | 
			
		||||
                    ("Pacific/Truk", "Pacific/Truk"),
 | 
			
		||||
                    ("Pacific/Wake", "Pacific/Wake"),
 | 
			
		||||
                    ("Pacific/Wallis", "Pacific/Wallis"),
 | 
			
		||||
                    ("Pacific/Yap", "Pacific/Yap"),
 | 
			
		||||
                    ("Poland", "Poland"),
 | 
			
		||||
                    ("Portugal", "Portugal"),
 | 
			
		||||
                    ("ROC", "ROC"),
 | 
			
		||||
                    ("ROK", "ROK"),
 | 
			
		||||
                    ("Singapore", "Singapore"),
 | 
			
		||||
                    ("Turkey", "Turkey"),
 | 
			
		||||
                    ("UCT", "UCT"),
 | 
			
		||||
                    ("US/Alaska", "US/Alaska"),
 | 
			
		||||
                    ("US/Aleutian", "US/Aleutian"),
 | 
			
		||||
                    ("US/Arizona", "US/Arizona"),
 | 
			
		||||
                    ("US/Central", "US/Central"),
 | 
			
		||||
                    ("US/East-Indiana", "US/East-Indiana"),
 | 
			
		||||
                    ("US/Eastern", "US/Eastern"),
 | 
			
		||||
                    ("US/Hawaii", "US/Hawaii"),
 | 
			
		||||
                    ("US/Indiana-Starke", "US/Indiana-Starke"),
 | 
			
		||||
                    ("US/Michigan", "US/Michigan"),
 | 
			
		||||
                    ("US/Mountain", "US/Mountain"),
 | 
			
		||||
                    ("US/Pacific", "US/Pacific"),
 | 
			
		||||
                    ("US/Samoa", "US/Samoa"),
 | 
			
		||||
                    ("UTC", "UTC"),
 | 
			
		||||
                    ("Universal", "Universal"),
 | 
			
		||||
                    ("W-SU", "W-SU"),
 | 
			
		||||
                    ("WET", "WET"),
 | 
			
		||||
                    ("Zulu", "Zulu"),
 | 
			
		||||
                ],
 | 
			
		||||
                default=django.utils.timezone.get_current_timezone,
 | 
			
		||||
                help_text="timezone used for the date",
 | 
			
		||||
                max_length=100,
 | 
			
		||||
                verbose_name="timezone",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="is_public",
 | 
			
		||||
            field=models.BooleanField(
 | 
			
		||||
                default=False,
 | 
			
		||||
                help_text="whether it is publicly available as podcast",
 | 
			
		||||
                verbose_name="public",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="stream",
 | 
			
		||||
            name="begin",
 | 
			
		||||
            field=models.TimeField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                help_text="used to define a time range this stream is played",
 | 
			
		||||
                null=True,
 | 
			
		||||
                verbose_name="begin",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="stream",
 | 
			
		||||
            name="end",
 | 
			
		||||
            field=models.TimeField(
 | 
			
		||||
                blank=True,
 | 
			
		||||
                help_text="used to define a time range this stream is played",
 | 
			
		||||
                null=True,
 | 
			
		||||
                verbose_name="end",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@ -0,0 +1,48 @@
 | 
			
		||||
# Generated by Django 4.1 on 2022-12-09 13:46
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0007_sound_is_downloadable_alter_page_pub_date_and_more"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterModelOptions(
 | 
			
		||||
            name="diffusion",
 | 
			
		||||
            options={
 | 
			
		||||
                "permissions": (
 | 
			
		||||
                    ("programming", "edit the diffusions' planification"),
 | 
			
		||||
                ),
 | 
			
		||||
                "verbose_name": "Diffusion",
 | 
			
		||||
                "verbose_name_plural": "Diffusions",
 | 
			
		||||
            },
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="track",
 | 
			
		||||
            name="album",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                default="", max_length=128, verbose_name="album"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="schedule",
 | 
			
		||||
            name="frequency",
 | 
			
		||||
            field=models.SmallIntegerField(
 | 
			
		||||
                choices=[
 | 
			
		||||
                    (0, "ponctual"),
 | 
			
		||||
                    (1, "1st {day} of the month"),
 | 
			
		||||
                    (2, "2nd {day} of the month"),
 | 
			
		||||
                    (4, "3rd {day} of the month"),
 | 
			
		||||
                    (8, "4th {day} of the month"),
 | 
			
		||||
                    (16, "last {day} of the month"),
 | 
			
		||||
                    (5, "1st and 3rd {day} of the month"),
 | 
			
		||||
                    (10, "2nd and 4th {day} of the month"),
 | 
			
		||||
                    (31, "{day}"),
 | 
			
		||||
                    (32, "one {day} on two"),
 | 
			
		||||
                ],
 | 
			
		||||
                verbose_name="frequency",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										19
									
								
								aircox/migrations/0009_track_year.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								aircox/migrations/0009_track_year.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
# Generated by Django 4.1 on 2022-12-09 13:50
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0008_alter_diffusion_options_track_album_and_more"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AddField(
 | 
			
		||||
            model_name="track",
 | 
			
		||||
            name="year",
 | 
			
		||||
            field=models.IntegerField(
 | 
			
		||||
                blank=True, null=True, verbose_name="year"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										19
									
								
								aircox/migrations/0010_alter_track_album.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								aircox/migrations/0010_alter_track_album.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
# Generated by Django 4.1 on 2022-12-09 18:13
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0009_track_year"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="track",
 | 
			
		||||
            name="album",
 | 
			
		||||
            field=models.CharField(
 | 
			
		||||
                blank=True, max_length=128, null=True, verbose_name="album"
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										48
									
								
								aircox/migrations/0011_usersettings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								aircox/migrations/0011_usersettings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
# Generated by Django 4.1 on 2022-12-11 12:24
 | 
			
		||||
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
			
		||||
        ("aircox", "0010_alter_track_album"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="UserSettings",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "id",
 | 
			
		||||
                    models.BigAutoField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        verbose_name="ID",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "playlist_editor_columns",
 | 
			
		||||
                    models.JSONField(verbose_name="Playlist Editor Columns"),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "playlist_editor_sep",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        max_length=16, verbose_name="Playlist Editor Separator"
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "user",
 | 
			
		||||
                    models.OneToOneField(
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        related_name="aircox_settings",
 | 
			
		||||
                        to=settings.AUTH_USER_MODEL,
 | 
			
		||||
                        verbose_name="User",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@ -0,0 +1,33 @@
 | 
			
		||||
# Generated by Django 4.1 on 2023-01-25 15:18
 | 
			
		||||
 | 
			
		||||
import aircox.models.sound
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("aircox", "0011_usersettings"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="sound",
 | 
			
		||||
            name="file",
 | 
			
		||||
            field=models.FileField(
 | 
			
		||||
                db_index=True,
 | 
			
		||||
                max_length=256,
 | 
			
		||||
                unique=True,
 | 
			
		||||
                upload_to=aircox.models.sound.Sound._upload_to,
 | 
			
		||||
                verbose_name="file",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="station",
 | 
			
		||||
            name="default",
 | 
			
		||||
            field=models.BooleanField(
 | 
			
		||||
                default=False,
 | 
			
		||||
                help_text="use this station as the main one.",
 | 
			
		||||
                verbose_name="default station",
 | 
			
		||||
            ),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										0
									
								
								aircox/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								aircox/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							@ -1,17 +1,11 @@
 | 
			
		||||
from . import signals
 | 
			
		||||
from .article import Article
 | 
			
		||||
from .episode import Diffusion, DiffusionQuerySet, Episode
 | 
			
		||||
from .diffusion import Diffusion, DiffusionQuerySet
 | 
			
		||||
from .episode import Episode
 | 
			
		||||
from .log import Log, LogArchiver, LogQuerySet
 | 
			
		||||
from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
 | 
			
		||||
from .program import (
 | 
			
		||||
    BaseRerun,
 | 
			
		||||
    BaseRerunQuerySet,
 | 
			
		||||
    Program,
 | 
			
		||||
    ProgramChildQuerySet,
 | 
			
		||||
    ProgramQuerySet,
 | 
			
		||||
    Schedule,
 | 
			
		||||
    Stream,
 | 
			
		||||
)
 | 
			
		||||
from .program import Program, ProgramChildQuerySet, ProgramQuerySet, Stream
 | 
			
		||||
from .schedule import Schedule
 | 
			
		||||
from .sound import Sound, SoundQuerySet, Track
 | 
			
		||||
from .station import Port, Station, StationQuerySet
 | 
			
		||||
from .user_settings import UserSettings
 | 
			
		||||
@ -36,8 +30,6 @@ __all__ = (
 | 
			
		||||
    "Stream",
 | 
			
		||||
    "Schedule",
 | 
			
		||||
    "ProgramChildQuerySet",
 | 
			
		||||
    "BaseRerun",
 | 
			
		||||
    "BaseRerunQuerySet",
 | 
			
		||||
    "Sound",
 | 
			
		||||
    "SoundQuerySet",
 | 
			
		||||
    "Track",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										282
									
								
								aircox/models/diffusion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								aircox/models/diffusion.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,282 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from aircox import utils
 | 
			
		||||
 | 
			
		||||
from .episode import Episode
 | 
			
		||||
from .schedule import Schedule
 | 
			
		||||
from .rerun import Rerun, RerunQuerySet
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("Diffusion", "DiffusionQuerySet")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionQuerySet(RerunQuerySet):
 | 
			
		||||
    def episode(self, episode=None, id=None):
 | 
			
		||||
        """Diffusions for this episode."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(episode=episode)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(episode__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def on_air(self):
 | 
			
		||||
        """On air diffusions."""
 | 
			
		||||
        return self.filter(type=Diffusion.TYPE_ON_AIR)
 | 
			
		||||
 | 
			
		||||
    # TODO: rename to `datetime`
 | 
			
		||||
    def now(self, now=None, order=True):
 | 
			
		||||
        """Diffusions occuring now."""
 | 
			
		||||
        now = now or tz.now()
 | 
			
		||||
        qs = self.filter(start__lte=now, end__gte=now).distinct()
 | 
			
		||||
        return qs.order_by("start") if order else qs
 | 
			
		||||
 | 
			
		||||
    def date(self, date=None, order=True):
 | 
			
		||||
        """Diffusions occuring date."""
 | 
			
		||||
        date = date or datetime.date.today()
 | 
			
		||||
        start = tz.datetime.combine(date, datetime.time())
 | 
			
		||||
        end = tz.datetime.combine(date, datetime.time(23, 59, 59, 999))
 | 
			
		||||
        # start = tz.get_current_timezone().localize(start)
 | 
			
		||||
        # end = tz.get_current_timezone().localize(end)
 | 
			
		||||
        qs = self.filter(start__range=(start, end))
 | 
			
		||||
        return qs.order_by("start") if order else qs
 | 
			
		||||
 | 
			
		||||
    def at(self, date, order=True):
 | 
			
		||||
        """Return diffusions at specified date or datetime."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.now(date, order)
 | 
			
		||||
            if isinstance(date, tz.datetime)
 | 
			
		||||
            else self.date(date, order)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def after(self, date=None):
 | 
			
		||||
        """Return a queryset of diffusions that happen after the given date
 | 
			
		||||
        (default: today)."""
 | 
			
		||||
        date = utils.date_or_default(date)
 | 
			
		||||
        if isinstance(date, tz.datetime):
 | 
			
		||||
            qs = self.filter(Q(start__gte=date) | Q(end__gte=date))
 | 
			
		||||
        else:
 | 
			
		||||
            qs = self.filter(Q(start__date__gte=date) | Q(end__date__gte=date))
 | 
			
		||||
        return qs.order_by("start")
 | 
			
		||||
 | 
			
		||||
    def before(self, date=None):
 | 
			
		||||
        """Return a queryset of diffusions that finish before the given date
 | 
			
		||||
        (default: today)."""
 | 
			
		||||
        date = utils.date_or_default(date)
 | 
			
		||||
        if isinstance(date, tz.datetime):
 | 
			
		||||
            qs = self.filter(start__lt=date)
 | 
			
		||||
        else:
 | 
			
		||||
            qs = self.filter(start__date__lt=date)
 | 
			
		||||
        return qs.order_by("start")
 | 
			
		||||
 | 
			
		||||
    def range(self, start, end):
 | 
			
		||||
        # FIXME can return dates that are out of range...
 | 
			
		||||
        return self.after(start).before(end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Diffusion(Rerun):
 | 
			
		||||
    """A Diffusion is an occurrence of a Program that is scheduled on the
 | 
			
		||||
    station's timetable. It can be a rerun of a previous diffusion. In such a
 | 
			
		||||
    case, use rerun's info instead of its own.
 | 
			
		||||
 | 
			
		||||
    A Diffusion without any rerun is named Episode (previously, a
 | 
			
		||||
    Diffusion was different from an Episode, but in the end, an
 | 
			
		||||
    episode only has a name, a linked program, and a list of sounds, so we
 | 
			
		||||
    finally merge theme).
 | 
			
		||||
 | 
			
		||||
    A Diffusion can have different types:
 | 
			
		||||
    - default: simple diffusion that is planified / did occurred
 | 
			
		||||
    - unconfirmed: a generated diffusion that has not been confirmed and thus
 | 
			
		||||
        is not yet planified
 | 
			
		||||
    - cancel: the diffusion has been canceled
 | 
			
		||||
    - stop: the diffusion has been manually stopped
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    objects = DiffusionQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    TYPE_ON_AIR = 0x00
 | 
			
		||||
    TYPE_UNCONFIRMED = 0x01
 | 
			
		||||
    TYPE_CANCEL = 0x02
 | 
			
		||||
    TYPE_CHOICES = (
 | 
			
		||||
        (TYPE_ON_AIR, _("on air")),
 | 
			
		||||
        (TYPE_UNCONFIRMED, _("not confirmed")),
 | 
			
		||||
        (TYPE_CANCEL, _("cancelled")),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    episode = models.ForeignKey(
 | 
			
		||||
        Episode,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        verbose_name=_("episode"),
 | 
			
		||||
    )
 | 
			
		||||
    schedule = models.ForeignKey(
 | 
			
		||||
        Schedule,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        verbose_name=_("schedule"),
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
    )
 | 
			
		||||
    type = models.SmallIntegerField(
 | 
			
		||||
        verbose_name=_("type"),
 | 
			
		||||
        default=TYPE_ON_AIR,
 | 
			
		||||
        choices=TYPE_CHOICES,
 | 
			
		||||
    )
 | 
			
		||||
    start = models.DateTimeField(_("start"), db_index=True)
 | 
			
		||||
    end = models.DateTimeField(_("end"), db_index=True)
 | 
			
		||||
    # port = models.ForeignKey(
 | 
			
		||||
    #    'self',
 | 
			
		||||
    #    verbose_name = _('port'),
 | 
			
		||||
    #    blank = True, null = True,
 | 
			
		||||
    #    on_delete=models.SET_NULL,
 | 
			
		||||
    #    help_text = _('use this input port'),
 | 
			
		||||
    # )
 | 
			
		||||
 | 
			
		||||
    item_template_name = "aircox/widgets/diffusion_item.html"
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Diffusion")
 | 
			
		||||
        verbose_name_plural = _("Diffusions")
 | 
			
		||||
        permissions = (
 | 
			
		||||
            ("programming", _("edit the diffusions' planification")),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        str_ = "{episode} - {date}".format(
 | 
			
		||||
            episode=self.episode and self.episode.title,
 | 
			
		||||
            date=self.local_start.strftime("%Y/%m/%d %H:%M%z"),
 | 
			
		||||
        )
 | 
			
		||||
        if self.initial:
 | 
			
		||||
            str_ += " ({})".format(_("rerun"))
 | 
			
		||||
        return str_
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
        if self.is_initial and self.episode != self._initial["episode"]:
 | 
			
		||||
            self.rerun_set.update(episode=self.episode, program=self.program)
 | 
			
		||||
 | 
			
		||||
    # def save(self, no_check=False, *args, **kwargs):
 | 
			
		||||
    # if self.start != self._initial['start'] or \
 | 
			
		||||
    #        self.end != self._initial['end']:
 | 
			
		||||
    #    self.check_conflicts()
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self):
 | 
			
		||||
        self.episode = self.initial.episode
 | 
			
		||||
        super().save_rerun()
 | 
			
		||||
 | 
			
		||||
    def save_initial(self):
 | 
			
		||||
        self.program = self.episode.program
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def duration(self):
 | 
			
		||||
        return self.end - self.start
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def date(self):
 | 
			
		||||
        """Return diffusion start as a date."""
 | 
			
		||||
 | 
			
		||||
        return utils.cast_date(self.start)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def local_start(self):
 | 
			
		||||
        """Return a version of self.date that is localized to self.timezone;
 | 
			
		||||
        This is needed since datetime are stored as UTC date and we want to get
 | 
			
		||||
        it as local time."""
 | 
			
		||||
 | 
			
		||||
        return tz.localtime(self.start, tz.get_current_timezone())
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def local_end(self):
 | 
			
		||||
        """Return a version of self.date that is localized to self.timezone;
 | 
			
		||||
        This is needed since datetime are stored as UTC date and we want to get
 | 
			
		||||
        it as local time."""
 | 
			
		||||
 | 
			
		||||
        return tz.localtime(self.end, tz.get_current_timezone())
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_now(self):
 | 
			
		||||
        """True if diffusion is currently running."""
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
        return (
 | 
			
		||||
            self.type == self.TYPE_ON_AIR
 | 
			
		||||
            and self.start <= now
 | 
			
		||||
            and self.end >= now
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_live(self):
 | 
			
		||||
        """True if Diffusion is live (False if there are sounds files)."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.type == self.TYPE_ON_AIR
 | 
			
		||||
            and not self.episode.sound_set.archive().count()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_playlist(self, **types):
 | 
			
		||||
        """Returns sounds as a playlist (list of *local* archive file path).
 | 
			
		||||
 | 
			
		||||
        The given arguments are passed to ``get_sounds``.
 | 
			
		||||
        """
 | 
			
		||||
        from .sound import Sound
 | 
			
		||||
 | 
			
		||||
        return list(
 | 
			
		||||
            self.get_sounds(**types)
 | 
			
		||||
            .filter(path__isnull=False, type=Sound.TYPE_ARCHIVE)
 | 
			
		||||
            .values_list("path", flat=True)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_sounds(self, **types):
 | 
			
		||||
        """Return a queryset of sounds related to this diffusion, ordered by
 | 
			
		||||
        type then path.
 | 
			
		||||
 | 
			
		||||
        **types: filter on the given sound types name, as `archive=True`
 | 
			
		||||
        """
 | 
			
		||||
        from .sound import Sound
 | 
			
		||||
 | 
			
		||||
        sounds = (self.initial or self).sound_set.order_by("type", "path")
 | 
			
		||||
        _in = [
 | 
			
		||||
            getattr(Sound.Type, name) for name, value in types.items() if value
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        return sounds.filter(type__in=_in)
 | 
			
		||||
 | 
			
		||||
    def is_date_in_range(self, date=None):
 | 
			
		||||
        """Return true if the given date is in the diffusion's start-end
 | 
			
		||||
        range."""
 | 
			
		||||
        date = date or tz.now()
 | 
			
		||||
 | 
			
		||||
        return self.start < date < self.end
 | 
			
		||||
 | 
			
		||||
    def get_conflicts(self):
 | 
			
		||||
        """Return conflicting diffusions queryset."""
 | 
			
		||||
 | 
			
		||||
        # conflicts=Diffusion.objects.filter(
 | 
			
		||||
        #    Q(start__lt=OuterRef('start'), end__gt=OuterRef('end')) |
 | 
			
		||||
        #    Q(start__gt=OuterRef('start'), start__lt=OuterRef('end'))
 | 
			
		||||
        # )
 | 
			
		||||
        # diffs= Diffusion.objects.annotate(conflict_with=Exists(conflicts))
 | 
			
		||||
        #                         .filter(conflict_with=True)
 | 
			
		||||
        return (
 | 
			
		||||
            Diffusion.objects.filter(
 | 
			
		||||
                Q(start__lt=self.start, end__gt=self.start)
 | 
			
		||||
                | Q(start__gt=self.start, start__lt=self.end)
 | 
			
		||||
            )
 | 
			
		||||
            .exclude(pk=self.pk)
 | 
			
		||||
            .distinct()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def check_conflicts(self):
 | 
			
		||||
        conflicts = self.get_conflicts()
 | 
			
		||||
        self.conflicts.set(conflicts)
 | 
			
		||||
 | 
			
		||||
    _initial = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self._initial = {
 | 
			
		||||
            "start": self.start,
 | 
			
		||||
            "end": self.end,
 | 
			
		||||
            "episode": getattr(self, "episode", None),
 | 
			
		||||
        }
 | 
			
		||||
@ -1,24 +1,13 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models import Q
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from easy_thumbnails.files import get_thumbnailer
 | 
			
		||||
 | 
			
		||||
from aircox.conf import settings
 | 
			
		||||
from aircox import utils
 | 
			
		||||
 | 
			
		||||
from .page import Page
 | 
			
		||||
from .program import (
 | 
			
		||||
    BaseRerun,
 | 
			
		||||
    BaseRerunQuerySet,
 | 
			
		||||
    ProgramChildQuerySet,
 | 
			
		||||
    Schedule,
 | 
			
		||||
)
 | 
			
		||||
from .program import ProgramChildQuerySet
 | 
			
		||||
 | 
			
		||||
__all__ = ("Episode", "Diffusion", "DiffusionQuerySet")
 | 
			
		||||
__all__ = ("Episode",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Episode(Page):
 | 
			
		||||
@ -90,269 +79,3 @@ class Episode(Page):
 | 
			
		||||
        return super().get_init_kwargs_from(
 | 
			
		||||
            page, title=title, program=page, **kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionQuerySet(BaseRerunQuerySet):
 | 
			
		||||
    def episode(self, episode=None, id=None):
 | 
			
		||||
        """Diffusions for this episode."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(episode=episode)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(episode__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def on_air(self):
 | 
			
		||||
        """On air diffusions."""
 | 
			
		||||
        return self.filter(type=Diffusion.TYPE_ON_AIR)
 | 
			
		||||
 | 
			
		||||
    # TODO: rename to `datetime`
 | 
			
		||||
    def now(self, now=None, order=True):
 | 
			
		||||
        """Diffusions occuring now."""
 | 
			
		||||
        now = now or tz.now()
 | 
			
		||||
        qs = self.filter(start__lte=now, end__gte=now).distinct()
 | 
			
		||||
        return qs.order_by("start") if order else qs
 | 
			
		||||
 | 
			
		||||
    def date(self, date=None, order=True):
 | 
			
		||||
        """Diffusions occuring date."""
 | 
			
		||||
        date = date or datetime.date.today()
 | 
			
		||||
        start = tz.datetime.combine(date, datetime.time())
 | 
			
		||||
        end = tz.datetime.combine(date, datetime.time(23, 59, 59, 999))
 | 
			
		||||
        # start = tz.get_current_timezone().localize(start)
 | 
			
		||||
        # end = tz.get_current_timezone().localize(end)
 | 
			
		||||
        qs = self.filter(start__range=(start, end))
 | 
			
		||||
        return qs.order_by("start") if order else qs
 | 
			
		||||
 | 
			
		||||
    def at(self, date, order=True):
 | 
			
		||||
        """Return diffusions at specified date or datetime."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.now(date, order)
 | 
			
		||||
            if isinstance(date, tz.datetime)
 | 
			
		||||
            else self.date(date, order)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def after(self, date=None):
 | 
			
		||||
        """Return a queryset of diffusions that happen after the given date
 | 
			
		||||
        (default: today)."""
 | 
			
		||||
        date = utils.date_or_default(date)
 | 
			
		||||
        if isinstance(date, tz.datetime):
 | 
			
		||||
            qs = self.filter(Q(start__gte=date) | Q(end__gte=date))
 | 
			
		||||
        else:
 | 
			
		||||
            qs = self.filter(Q(start__date__gte=date) | Q(end__date__gte=date))
 | 
			
		||||
        return qs.order_by("start")
 | 
			
		||||
 | 
			
		||||
    def before(self, date=None):
 | 
			
		||||
        """Return a queryset of diffusions that finish before the given date
 | 
			
		||||
        (default: today)."""
 | 
			
		||||
        date = utils.date_or_default(date)
 | 
			
		||||
        if isinstance(date, tz.datetime):
 | 
			
		||||
            qs = self.filter(start__lt=date)
 | 
			
		||||
        else:
 | 
			
		||||
            qs = self.filter(start__date__lt=date)
 | 
			
		||||
        return qs.order_by("start")
 | 
			
		||||
 | 
			
		||||
    def range(self, start, end):
 | 
			
		||||
        # FIXME can return dates that are out of range...
 | 
			
		||||
        return self.after(start).before(end)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Diffusion(BaseRerun):
 | 
			
		||||
    """A Diffusion is an occurrence of a Program that is scheduled on the
 | 
			
		||||
    station's timetable. It can be a rerun of a previous diffusion. In such a
 | 
			
		||||
    case, use rerun's info instead of its own.
 | 
			
		||||
 | 
			
		||||
    A Diffusion without any rerun is named Episode (previously, a
 | 
			
		||||
    Diffusion was different from an Episode, but in the end, an
 | 
			
		||||
    episode only has a name, a linked program, and a list of sounds, so we
 | 
			
		||||
    finally merge theme).
 | 
			
		||||
 | 
			
		||||
    A Diffusion can have different types:
 | 
			
		||||
    - default: simple diffusion that is planified / did occurred
 | 
			
		||||
    - unconfirmed: a generated diffusion that has not been confirmed and thus
 | 
			
		||||
        is not yet planified
 | 
			
		||||
    - cancel: the diffusion has been canceled
 | 
			
		||||
    - stop: the diffusion has been manually stopped
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    objects = DiffusionQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    TYPE_ON_AIR = 0x00
 | 
			
		||||
    TYPE_UNCONFIRMED = 0x01
 | 
			
		||||
    TYPE_CANCEL = 0x02
 | 
			
		||||
    TYPE_CHOICES = (
 | 
			
		||||
        (TYPE_ON_AIR, _("on air")),
 | 
			
		||||
        (TYPE_UNCONFIRMED, _("not confirmed")),
 | 
			
		||||
        (TYPE_CANCEL, _("cancelled")),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    episode = models.ForeignKey(
 | 
			
		||||
        Episode,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        verbose_name=_("episode"),
 | 
			
		||||
    )
 | 
			
		||||
    schedule = models.ForeignKey(
 | 
			
		||||
        Schedule,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        verbose_name=_("schedule"),
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
    )
 | 
			
		||||
    type = models.SmallIntegerField(
 | 
			
		||||
        verbose_name=_("type"),
 | 
			
		||||
        default=TYPE_ON_AIR,
 | 
			
		||||
        choices=TYPE_CHOICES,
 | 
			
		||||
    )
 | 
			
		||||
    start = models.DateTimeField(_("start"), db_index=True)
 | 
			
		||||
    end = models.DateTimeField(_("end"), db_index=True)
 | 
			
		||||
    # port = models.ForeignKey(
 | 
			
		||||
    #    'self',
 | 
			
		||||
    #    verbose_name = _('port'),
 | 
			
		||||
    #    blank = True, null = True,
 | 
			
		||||
    #    on_delete=models.SET_NULL,
 | 
			
		||||
    #    help_text = _('use this input port'),
 | 
			
		||||
    # )
 | 
			
		||||
 | 
			
		||||
    item_template_name = "aircox/widgets/diffusion_item.html"
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Diffusion")
 | 
			
		||||
        verbose_name_plural = _("Diffusions")
 | 
			
		||||
        permissions = (
 | 
			
		||||
            ("programming", _("edit the diffusions' planification")),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        str_ = "{episode} - {date}".format(
 | 
			
		||||
            episode=self.episode and self.episode.title,
 | 
			
		||||
            date=self.local_start.strftime("%Y/%m/%d %H:%M%z"),
 | 
			
		||||
        )
 | 
			
		||||
        if self.initial:
 | 
			
		||||
            str_ += " ({})".format(_("rerun"))
 | 
			
		||||
        return str_
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
        if self.is_initial and self.episode != self._initial["episode"]:
 | 
			
		||||
            self.rerun_set.update(episode=self.episode, program=self.program)
 | 
			
		||||
 | 
			
		||||
    # def save(self, no_check=False, *args, **kwargs):
 | 
			
		||||
    # if self.start != self._initial['start'] or \
 | 
			
		||||
    #        self.end != self._initial['end']:
 | 
			
		||||
    #    self.check_conflicts()
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self):
 | 
			
		||||
        self.episode = self.initial.episode
 | 
			
		||||
        self.program = self.episode.program
 | 
			
		||||
 | 
			
		||||
    def save_initial(self):
 | 
			
		||||
        self.program = self.episode.program
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def duration(self):
 | 
			
		||||
        return self.end - self.start
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def date(self):
 | 
			
		||||
        """Return diffusion start as a date."""
 | 
			
		||||
 | 
			
		||||
        return utils.cast_date(self.start)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def local_start(self):
 | 
			
		||||
        """Return a version of self.date that is localized to self.timezone;
 | 
			
		||||
        This is needed since datetime are stored as UTC date and we want to get
 | 
			
		||||
        it as local time."""
 | 
			
		||||
 | 
			
		||||
        return tz.localtime(self.start, tz.get_current_timezone())
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def local_end(self):
 | 
			
		||||
        """Return a version of self.date that is localized to self.timezone;
 | 
			
		||||
        This is needed since datetime are stored as UTC date and we want to get
 | 
			
		||||
        it as local time."""
 | 
			
		||||
 | 
			
		||||
        return tz.localtime(self.end, tz.get_current_timezone())
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_now(self):
 | 
			
		||||
        """True if diffusion is currently running."""
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
        return (
 | 
			
		||||
            self.type == self.TYPE_ON_AIR
 | 
			
		||||
            and self.start <= now
 | 
			
		||||
            and self.end >= now
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_live(self):
 | 
			
		||||
        """True if Diffusion is live (False if there are sounds files)."""
 | 
			
		||||
        return (
 | 
			
		||||
            self.type == self.TYPE_ON_AIR
 | 
			
		||||
            and not self.episode.sound_set.archive().count()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_playlist(self, **types):
 | 
			
		||||
        """Returns sounds as a playlist (list of *local* archive file path).
 | 
			
		||||
 | 
			
		||||
        The given arguments are passed to ``get_sounds``.
 | 
			
		||||
        """
 | 
			
		||||
        from .sound import Sound
 | 
			
		||||
 | 
			
		||||
        return list(
 | 
			
		||||
            self.get_sounds(**types)
 | 
			
		||||
            .filter(path__isnull=False, type=Sound.TYPE_ARCHIVE)
 | 
			
		||||
            .values_list("path", flat=True)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_sounds(self, **types):
 | 
			
		||||
        """Return a queryset of sounds related to this diffusion, ordered by
 | 
			
		||||
        type then path.
 | 
			
		||||
 | 
			
		||||
        **types: filter on the given sound types name, as `archive=True`
 | 
			
		||||
        """
 | 
			
		||||
        from .sound import Sound
 | 
			
		||||
 | 
			
		||||
        sounds = (self.initial or self).sound_set.order_by("type", "path")
 | 
			
		||||
        _in = [
 | 
			
		||||
            getattr(Sound.Type, name) for name, value in types.items() if value
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        return sounds.filter(type__in=_in)
 | 
			
		||||
 | 
			
		||||
    def is_date_in_range(self, date=None):
 | 
			
		||||
        """Return true if the given date is in the diffusion's start-end
 | 
			
		||||
        range."""
 | 
			
		||||
        date = date or tz.now()
 | 
			
		||||
 | 
			
		||||
        return self.start < date < self.end
 | 
			
		||||
 | 
			
		||||
    def get_conflicts(self):
 | 
			
		||||
        """Return conflicting diffusions queryset."""
 | 
			
		||||
 | 
			
		||||
        # conflicts=Diffusion.objects.filter(
 | 
			
		||||
        #    Q(start__lt=OuterRef('start'), end__gt=OuterRef('end')) |
 | 
			
		||||
        #    Q(start__gt=OuterRef('start'), start__lt=OuterRef('end'))
 | 
			
		||||
        # )
 | 
			
		||||
        # diffs= Diffusion.objects.annotate(conflict_with=Exists(conflicts))
 | 
			
		||||
        #                         .filter(conflict_with=True)
 | 
			
		||||
        return (
 | 
			
		||||
            Diffusion.objects.filter(
 | 
			
		||||
                Q(start__lt=self.start, end__gt=self.start)
 | 
			
		||||
                | Q(start__gt=self.start, start__lt=self.end)
 | 
			
		||||
            )
 | 
			
		||||
            .exclude(pk=self.pk)
 | 
			
		||||
            .distinct()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def check_conflicts(self):
 | 
			
		||||
        conflicts = self.get_conflicts()
 | 
			
		||||
        self.conflicts.set(conflicts)
 | 
			
		||||
 | 
			
		||||
    _initial = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self._initial = {
 | 
			
		||||
            "start": self.start,
 | 
			
		||||
            "end": self.end,
 | 
			
		||||
            "episode": getattr(self, "episode", None),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,7 @@ from django.utils.translation import gettext_lazy as _
 | 
			
		||||
from aircox.conf import settings
 | 
			
		||||
 | 
			
		||||
__all__ = ("Settings", "settings")
 | 
			
		||||
from .episode import Diffusion
 | 
			
		||||
from .diffusion import Diffusion
 | 
			
		||||
from .sound import Sound, Track
 | 
			
		||||
from .station import Station
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,13 @@
 | 
			
		||||
import calendar
 | 
			
		||||
import logging
 | 
			
		||||
import os
 | 
			
		||||
import shutil
 | 
			
		||||
from collections import OrderedDict
 | 
			
		||||
from enum import IntEnum
 | 
			
		||||
 | 
			
		||||
import pytz
 | 
			
		||||
from django.conf import settings as conf
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.db.models import F
 | 
			
		||||
from django.db.models.functions import Concat, Substr
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from aircox import utils
 | 
			
		||||
from aircox.conf import settings
 | 
			
		||||
 | 
			
		||||
from .page import Page, PageQuerySet
 | 
			
		||||
@ -26,12 +18,9 @@ logger = logging.getLogger("aircox")
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "Program",
 | 
			
		||||
    "ProgramChildQuerySet",
 | 
			
		||||
    "ProgramQuerySet",
 | 
			
		||||
    "Stream",
 | 
			
		||||
    "Schedule",
 | 
			
		||||
    "ProgramChildQuerySet",
 | 
			
		||||
    "BaseRerun",
 | 
			
		||||
    "BaseRerunQuerySet",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -167,352 +156,6 @@ class ProgramChildQuerySet(PageQuerySet):
 | 
			
		||||
        return self.parent(program, id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseRerunQuerySet(models.QuerySet):
 | 
			
		||||
    """Queryset for BaseRerun (sub)classes."""
 | 
			
		||||
 | 
			
		||||
    def station(self, station=None, id=None):
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(program__station=station)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(program__station__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def program(self, program=None, id=None):
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(program=program)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(program__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def rerun(self):
 | 
			
		||||
        return self.filter(initial__isnull=False)
 | 
			
		||||
 | 
			
		||||
    def initial(self):
 | 
			
		||||
        return self.filter(initial__isnull=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BaseRerun(models.Model):
 | 
			
		||||
    """Abstract model offering rerun facilities.
 | 
			
		||||
 | 
			
		||||
    Assume `start` is a datetime field or attribute implemented by
 | 
			
		||||
    subclass.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    program = models.ForeignKey(
 | 
			
		||||
        Program,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        db_index=True,
 | 
			
		||||
        verbose_name=_("related program"),
 | 
			
		||||
    )
 | 
			
		||||
    initial = models.ForeignKey(
 | 
			
		||||
        "self",
 | 
			
		||||
        models.SET_NULL,
 | 
			
		||||
        related_name="rerun_set",
 | 
			
		||||
        verbose_name=_("rerun of"),
 | 
			
		||||
        limit_choices_to={"initial__isnull": True},
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        db_index=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    objects = BaseRerunQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if self.initial is not None:
 | 
			
		||||
            self.initial = self.initial.get_initial()
 | 
			
		||||
        if self.initial == self:
 | 
			
		||||
            self.initial = None
 | 
			
		||||
 | 
			
		||||
        if self.is_rerun:
 | 
			
		||||
            self.save_rerun()
 | 
			
		||||
        else:
 | 
			
		||||
            self.save_initial()
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def save_initial(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_initial(self):
 | 
			
		||||
        return self.initial is None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_rerun(self):
 | 
			
		||||
        return self.initial is not None
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        """Return the initial schedule (self or initial)"""
 | 
			
		||||
        return self if self.initial is None else self.initial.get_initial()
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        super().clean()
 | 
			
		||||
        if self.initial is not None and self.initial.start >= self.start:
 | 
			
		||||
            raise ValidationError(
 | 
			
		||||
                {"initial": _("rerun must happen after original")}
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ? BIG FIXME: self.date is still used as datetime
 | 
			
		||||
class Schedule(BaseRerun):
 | 
			
		||||
    """A Schedule defines time slots of programs' diffusions.
 | 
			
		||||
 | 
			
		||||
    It can be an initial run or a rerun (in such case it is linked to
 | 
			
		||||
    the related schedule).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Frequency for schedules. Basically, it is a mask of bits where each bit
 | 
			
		||||
    # is a week. Bits > rank 5 are used for special schedules.
 | 
			
		||||
    # Important: the first week is always the first week where the weekday of
 | 
			
		||||
    # the schedule is present.
 | 
			
		||||
    # For ponctual programs, there is no need for a schedule, only a diffusion
 | 
			
		||||
    class Frequency(IntEnum):
 | 
			
		||||
        ponctual = 0b000000
 | 
			
		||||
        first = 0b000001
 | 
			
		||||
        second = 0b000010
 | 
			
		||||
        third = 0b000100
 | 
			
		||||
        fourth = 0b001000
 | 
			
		||||
        last = 0b010000
 | 
			
		||||
        first_and_third = 0b000101
 | 
			
		||||
        second_and_fourth = 0b001010
 | 
			
		||||
        every = 0b011111
 | 
			
		||||
        one_on_two = 0b100000
 | 
			
		||||
 | 
			
		||||
    date = models.DateField(
 | 
			
		||||
        _("date"),
 | 
			
		||||
        help_text=_("date of the first diffusion"),
 | 
			
		||||
    )
 | 
			
		||||
    time = models.TimeField(
 | 
			
		||||
        _("time"),
 | 
			
		||||
        help_text=_("start time"),
 | 
			
		||||
    )
 | 
			
		||||
    timezone = models.CharField(
 | 
			
		||||
        _("timezone"),
 | 
			
		||||
        default=tz.get_current_timezone,
 | 
			
		||||
        max_length=100,
 | 
			
		||||
        choices=[(x, x) for x in pytz.all_timezones],
 | 
			
		||||
        help_text=_("timezone used for the date"),
 | 
			
		||||
    )
 | 
			
		||||
    duration = models.TimeField(
 | 
			
		||||
        _("duration"),
 | 
			
		||||
        help_text=_("regular duration"),
 | 
			
		||||
    )
 | 
			
		||||
    frequency = models.SmallIntegerField(
 | 
			
		||||
        _("frequency"),
 | 
			
		||||
        choices=[
 | 
			
		||||
            (
 | 
			
		||||
                int(y),
 | 
			
		||||
                {
 | 
			
		||||
                    "ponctual": _("ponctual"),
 | 
			
		||||
                    "first": _("1st {day} of the month"),
 | 
			
		||||
                    "second": _("2nd {day} of the month"),
 | 
			
		||||
                    "third": _("3rd {day} of the month"),
 | 
			
		||||
                    "fourth": _("4th {day} of the month"),
 | 
			
		||||
                    "last": _("last {day} of the month"),
 | 
			
		||||
                    "first_and_third": _("1st and 3rd {day} of the month"),
 | 
			
		||||
                    "second_and_fourth": _("2nd and 4th {day} of the month"),
 | 
			
		||||
                    "every": _("{day}"),
 | 
			
		||||
                    "one_on_two": _("one {day} on two"),
 | 
			
		||||
                }[x],
 | 
			
		||||
            )
 | 
			
		||||
            for x, y in Frequency.__members__.items()
 | 
			
		||||
        ],
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Schedule")
 | 
			
		||||
        verbose_name_plural = _("Schedules")
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{} - {}, {}".format(
 | 
			
		||||
            self.program.title,
 | 
			
		||||
            self.get_frequency_verbose(),
 | 
			
		||||
            self.time.strftime("%H:%M"),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self, *args, **kwargs):
 | 
			
		||||
        self.program = self.initial.program
 | 
			
		||||
        self.duration = self.initial.duration
 | 
			
		||||
        self.frequency = self.initial.frequency
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def tz(self):
 | 
			
		||||
        """Pytz timezone of the schedule."""
 | 
			
		||||
        import pytz
 | 
			
		||||
 | 
			
		||||
        return pytz.timezone(self.timezone)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """Datetime of the start (timezone unaware)"""
 | 
			
		||||
        return tz.datetime.combine(self.date, self.time)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def end(self):
 | 
			
		||||
        """Datetime of the end."""
 | 
			
		||||
        return self.start + utils.to_timedelta(self.duration)
 | 
			
		||||
 | 
			
		||||
    def get_frequency_verbose(self):
 | 
			
		||||
        """Return frequency formated for display."""
 | 
			
		||||
        from django.template.defaultfilters import date
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            self.get_frequency_display()
 | 
			
		||||
            .format(day=date(self.date, "l"))
 | 
			
		||||
            .capitalize()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    # initial cached data
 | 
			
		||||
    __initial = None
 | 
			
		||||
 | 
			
		||||
    def changed(self, fields=["date", "duration", "frequency", "timezone"]):
 | 
			
		||||
        initial = self._Schedule__initial
 | 
			
		||||
 | 
			
		||||
        if not initial:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        this = self.__dict__
 | 
			
		||||
 | 
			
		||||
        for field in fields:
 | 
			
		||||
            if initial.get(field) != this.get(field):
 | 
			
		||||
                return True
 | 
			
		||||
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def normalize(self, date):
 | 
			
		||||
        """Return a datetime set to schedule's time for the provided date,
 | 
			
		||||
        handling timezone (based on schedule's timezone)."""
 | 
			
		||||
        date = tz.datetime.combine(date, self.time)
 | 
			
		||||
        return self.tz.normalize(self.tz.localize(date))
 | 
			
		||||
 | 
			
		||||
    def dates_of_month(self, date):
 | 
			
		||||
        """Return normalized diffusion dates of provided date's month."""
 | 
			
		||||
        if self.frequency == Schedule.Frequency.ponctual:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        sched_wday, freq = self.date.weekday(), self.frequency
 | 
			
		||||
        date = date.replace(day=1)
 | 
			
		||||
 | 
			
		||||
        # last of the month
 | 
			
		||||
        if freq == Schedule.Frequency.last:
 | 
			
		||||
            date = date.replace(
 | 
			
		||||
                day=calendar.monthrange(date.year, date.month)[1]
 | 
			
		||||
            )
 | 
			
		||||
            date_wday = date.weekday()
 | 
			
		||||
 | 
			
		||||
            # end of month before the wanted weekday: move one week back
 | 
			
		||||
            if date_wday < sched_wday:
 | 
			
		||||
                date -= tz.timedelta(days=7)
 | 
			
		||||
            date += tz.timedelta(days=sched_wday - date_wday)
 | 
			
		||||
            return [self.normalize(date)]
 | 
			
		||||
 | 
			
		||||
        # move to the first day of the month that matches the schedule's
 | 
			
		||||
        # weekday. Check on SO#3284452 for the formula
 | 
			
		||||
        date_wday, month = date.weekday(), date.month
 | 
			
		||||
        date += tz.timedelta(
 | 
			
		||||
            days=(7 if date_wday > sched_wday else 0) - date_wday + sched_wday
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if freq == Schedule.Frequency.one_on_two:
 | 
			
		||||
            # - adjust date with modulo 14 (= 2 weeks in days)
 | 
			
		||||
            # - there are max 3 "weeks on two" per month
 | 
			
		||||
            if (date - self.date).days % 14:
 | 
			
		||||
                date += tz.timedelta(days=7)
 | 
			
		||||
            dates = (date + tz.timedelta(days=14 * i) for i in range(0, 3))
 | 
			
		||||
        else:
 | 
			
		||||
            dates = (
 | 
			
		||||
                date + tz.timedelta(days=7 * week)
 | 
			
		||||
                for week in range(0, 5)
 | 
			
		||||
                if freq & (0b1 << week)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return [self.normalize(date) for date in dates if date.month == month]
 | 
			
		||||
 | 
			
		||||
    def _exclude_existing_date(self, dates):
 | 
			
		||||
        from .episode import Diffusion
 | 
			
		||||
 | 
			
		||||
        saved = set(
 | 
			
		||||
            Diffusion.objects.filter(start__in=dates).values_list(
 | 
			
		||||
                "start", flat=True
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        return [date for date in dates if date not in saved]
 | 
			
		||||
 | 
			
		||||
    def diffusions_of_month(self, date):
 | 
			
		||||
        """Get episodes and diffusions for month of provided date, including
 | 
			
		||||
        reruns.
 | 
			
		||||
 | 
			
		||||
        :returns: tuple([Episode], [Diffusion])
 | 
			
		||||
        """
 | 
			
		||||
        from .episode import Diffusion, Episode
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            self.initial is not None
 | 
			
		||||
            or self.frequency == Schedule.Frequency.ponctual
 | 
			
		||||
        ):
 | 
			
		||||
            return [], []
 | 
			
		||||
 | 
			
		||||
        # dates for self and reruns as (date, initial)
 | 
			
		||||
        reruns = [
 | 
			
		||||
            (rerun, rerun.date - self.date) for rerun in self.rerun_set.all()
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        dates = OrderedDict((date, None) for date in self.dates_of_month(date))
 | 
			
		||||
        dates.update(
 | 
			
		||||
            [
 | 
			
		||||
                (rerun.normalize(date.date() + delta), date)
 | 
			
		||||
                for date in dates.keys()
 | 
			
		||||
                for rerun, delta in reruns
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # remove dates corresponding to existing diffusions
 | 
			
		||||
        saved = set(
 | 
			
		||||
            Diffusion.objects.filter(
 | 
			
		||||
                start__in=dates.keys(), program=self.program, schedule=self
 | 
			
		||||
            ).values_list("start", flat=True)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # make diffs
 | 
			
		||||
        duration = utils.to_timedelta(self.duration)
 | 
			
		||||
        diffusions = {}
 | 
			
		||||
        episodes = {}
 | 
			
		||||
 | 
			
		||||
        for date, initial in dates.items():
 | 
			
		||||
            if date in saved:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if initial is None:
 | 
			
		||||
                episode = Episode.from_page(self.program, date=date)
 | 
			
		||||
                episode.date = date
 | 
			
		||||
                episodes[date] = episode
 | 
			
		||||
            else:
 | 
			
		||||
                episode = episodes[initial]
 | 
			
		||||
                initial = diffusions[initial]
 | 
			
		||||
 | 
			
		||||
            diffusions[date] = Diffusion(
 | 
			
		||||
                episode=episode,
 | 
			
		||||
                schedule=self,
 | 
			
		||||
                type=Diffusion.TYPE_ON_AIR,
 | 
			
		||||
                initial=initial,
 | 
			
		||||
                start=date,
 | 
			
		||||
                end=date + duration,
 | 
			
		||||
            )
 | 
			
		||||
        return episodes.values(), diffusions.values()
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # TODO/FIXME: use validators?
 | 
			
		||||
        if self.initial is not None and self.date > self.date:
 | 
			
		||||
            raise ValueError("initial must be later")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Stream(models.Model):
 | 
			
		||||
    """When there are no program scheduled, it is possible to play sounds in
 | 
			
		||||
    order to avoid blanks. A Stream is a Program that plays this role, and
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										106
									
								
								aircox/models/rerun.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								aircox/models/rerun.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,106 @@
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from .program import Program
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "Rerun",
 | 
			
		||||
    "RerunQuerySet",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RerunQuerySet(models.QuerySet):
 | 
			
		||||
    """Queryset for Rerun (sub)classes."""
 | 
			
		||||
 | 
			
		||||
    def station(self, station=None, id=None):
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(program__station=station)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(program__station__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def program(self, program=None, id=None):
 | 
			
		||||
        return (
 | 
			
		||||
            self.filter(program=program)
 | 
			
		||||
            if id is None
 | 
			
		||||
            else self.filter(program__id=id)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def rerun(self):
 | 
			
		||||
        return self.filter(initial__isnull=False)
 | 
			
		||||
 | 
			
		||||
    def initial(self):
 | 
			
		||||
        return self.filter(initial__isnull=True)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Rerun(models.Model):
 | 
			
		||||
    """Abstract model offering rerun facilities.
 | 
			
		||||
 | 
			
		||||
    Assume `start` is a datetime field or attribute implemented by
 | 
			
		||||
    subclass.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    program = models.ForeignKey(
 | 
			
		||||
        Program,
 | 
			
		||||
        models.CASCADE,
 | 
			
		||||
        db_index=True,
 | 
			
		||||
        verbose_name=_("related program"),
 | 
			
		||||
    )
 | 
			
		||||
    initial = models.ForeignKey(
 | 
			
		||||
        "self",
 | 
			
		||||
        models.SET_NULL,
 | 
			
		||||
        related_name="rerun_set",
 | 
			
		||||
        verbose_name=_("rerun of"),
 | 
			
		||||
        limit_choices_to={"initial__isnull": True},
 | 
			
		||||
        blank=True,
 | 
			
		||||
        null=True,
 | 
			
		||||
        db_index=True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    objects = RerunQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_initial(self):
 | 
			
		||||
        return self.initial is None
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def is_rerun(self):
 | 
			
		||||
        return self.initial is not None
 | 
			
		||||
 | 
			
		||||
    def get_initial(self):
 | 
			
		||||
        """Return the initial schedule (self or initial)"""
 | 
			
		||||
        return self if self.initial is None else self.initial.get_initial()
 | 
			
		||||
 | 
			
		||||
    def clean(self):
 | 
			
		||||
        super().clean()
 | 
			
		||||
        if (
 | 
			
		||||
            hasattr(self, "start")
 | 
			
		||||
            and self.initial is not None
 | 
			
		||||
            and self.initial.start >= self.start
 | 
			
		||||
        ):
 | 
			
		||||
            raise ValidationError(
 | 
			
		||||
                {"initial": _("rerun must happen after original")}
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self):
 | 
			
		||||
        self.program = self.initial.program
 | 
			
		||||
 | 
			
		||||
    def save_initial(self):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if self.initial is not None:
 | 
			
		||||
            self.initial = self.initial.get_initial()
 | 
			
		||||
        if self.initial == self:
 | 
			
		||||
            self.initial = None
 | 
			
		||||
 | 
			
		||||
        if self.is_rerun:
 | 
			
		||||
            self.save_rerun()
 | 
			
		||||
        else:
 | 
			
		||||
            self.save_initial()
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
							
								
								
									
										217
									
								
								aircox/models/schedule.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								aircox/models/schedule.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,217 @@
 | 
			
		||||
import calendar
 | 
			
		||||
 | 
			
		||||
import pytz
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.utils.translation import gettext_lazy as _
 | 
			
		||||
 | 
			
		||||
from aircox import utils
 | 
			
		||||
 | 
			
		||||
from .rerun import Rerun
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ("Schedule",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# ? BIG FIXME: self.date is still used as datetime
 | 
			
		||||
class Schedule(Rerun):
 | 
			
		||||
    """A Schedule defines time slots of programs' diffusions.
 | 
			
		||||
 | 
			
		||||
    It can be an initial run or a rerun (in such case it is linked to
 | 
			
		||||
    the related schedule).
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    # Frequency for schedules. Basically, it is a mask of bits where each bit
 | 
			
		||||
    # is a week. Bits > rank 5 are used for special schedules.
 | 
			
		||||
    # Important: the first week is always the first week where the weekday of
 | 
			
		||||
    # the schedule is present.
 | 
			
		||||
    # For ponctual programs, there is no need for a schedule, only a diffusion
 | 
			
		||||
    class Frequency(models.IntegerChoices):
 | 
			
		||||
        ponctual = 0b000000, _("ponctual")
 | 
			
		||||
        first = 0b000001, _("1st {day} of the month")
 | 
			
		||||
        second = 0b000010, _("2nd {day} of the month")
 | 
			
		||||
        third = 0b000100, _("3rd {day} of the month")
 | 
			
		||||
        fourth = 0b001000, _("4th {day} of the month")
 | 
			
		||||
        last = 0b010000, _("last {day} of the month")
 | 
			
		||||
        first_and_third = 0b000101, _("1st and 3rd {day} of the month")
 | 
			
		||||
        second_and_fourth = 0b001010, _("2nd and 4th {day} of the month")
 | 
			
		||||
        every = 0b011111, _("{day}")
 | 
			
		||||
        one_on_two = 0b100000, _("one {day} on two")
 | 
			
		||||
 | 
			
		||||
    date = models.DateField(
 | 
			
		||||
        _("date"),
 | 
			
		||||
        help_text=_("date of the first diffusion"),
 | 
			
		||||
    )
 | 
			
		||||
    time = models.TimeField(
 | 
			
		||||
        _("time"),
 | 
			
		||||
        help_text=_("start time"),
 | 
			
		||||
    )
 | 
			
		||||
    timezone = models.CharField(
 | 
			
		||||
        _("timezone"),
 | 
			
		||||
        default=lambda: tz.get_current_timezone().zone,
 | 
			
		||||
        max_length=100,
 | 
			
		||||
        choices=[(x, x) for x in pytz.all_timezones],
 | 
			
		||||
        help_text=_("timezone used for the date"),
 | 
			
		||||
    )
 | 
			
		||||
    duration = models.TimeField(
 | 
			
		||||
        _("duration"),
 | 
			
		||||
        help_text=_("regular duration"),
 | 
			
		||||
    )
 | 
			
		||||
    frequency = models.SmallIntegerField(
 | 
			
		||||
        _("frequency"),
 | 
			
		||||
        choices=Frequency.choices,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _("Schedule")
 | 
			
		||||
        verbose_name_plural = _("Schedules")
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return "{} - {}, {}".format(
 | 
			
		||||
            self.program.title,
 | 
			
		||||
            self.get_frequency_display(),
 | 
			
		||||
            self.time.strftime("%H:%M"),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def save_rerun(self):
 | 
			
		||||
        super().save_rerun()
 | 
			
		||||
        self.duration = self.initial.duration
 | 
			
		||||
        self.frequency = self.initial.frequency
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def tz(self):
 | 
			
		||||
        """Pytz timezone of the schedule."""
 | 
			
		||||
        import pytz
 | 
			
		||||
 | 
			
		||||
        return pytz.timezone(self.timezone)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def start(self):
 | 
			
		||||
        """Datetime of the start (timezone unaware)"""
 | 
			
		||||
        return tz.datetime.combine(self.date, self.time)
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def end(self):
 | 
			
		||||
        """Datetime of the end."""
 | 
			
		||||
        return self.start + utils.to_timedelta(self.duration)
 | 
			
		||||
 | 
			
		||||
    def get_frequency_display(self):
 | 
			
		||||
        """Return frequency formated for display."""
 | 
			
		||||
        from django.template.defaultfilters import date
 | 
			
		||||
 | 
			
		||||
        return (
 | 
			
		||||
            self._get_FIELD_display(self._meta.get_field("frequency"))
 | 
			
		||||
            .format(day=date(self.date, "l"))
 | 
			
		||||
            .capitalize()
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def normalize(self, date):
 | 
			
		||||
        """Return a datetime set to schedule's time for the provided date,
 | 
			
		||||
        handling timezone (based on schedule's timezone)."""
 | 
			
		||||
        date = tz.datetime.combine(date, self.time)
 | 
			
		||||
        return self.tz.normalize(self.tz.localize(date))
 | 
			
		||||
 | 
			
		||||
    def dates_of_month(self, date):
 | 
			
		||||
        """Return normalized diffusion dates of provided date's month."""
 | 
			
		||||
        if self.frequency == Schedule.Frequency.ponctual:
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        sched_wday, freq = self.date.weekday(), self.frequency
 | 
			
		||||
        date = date.replace(day=1)
 | 
			
		||||
 | 
			
		||||
        # last of the month
 | 
			
		||||
        if freq == Schedule.Frequency.last:
 | 
			
		||||
            date = date.replace(
 | 
			
		||||
                day=calendar.monthrange(date.year, date.month)[1]
 | 
			
		||||
            )
 | 
			
		||||
            date_wday = date.weekday()
 | 
			
		||||
 | 
			
		||||
            # end of month before the wanted weekday: move one week back
 | 
			
		||||
            if date_wday < sched_wday:
 | 
			
		||||
                date -= tz.timedelta(days=7)
 | 
			
		||||
            date += tz.timedelta(days=sched_wday - date_wday)
 | 
			
		||||
            return [self.normalize(date)]
 | 
			
		||||
 | 
			
		||||
        # move to the first day of the month that matches the schedule's
 | 
			
		||||
        # weekday. Check on SO#3284452 for the formula
 | 
			
		||||
        date_wday, month = date.weekday(), date.month
 | 
			
		||||
        date += tz.timedelta(
 | 
			
		||||
            days=(7 if date_wday > sched_wday else 0) - date_wday + sched_wday
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        if freq == Schedule.Frequency.one_on_two:
 | 
			
		||||
            # - adjust date with modulo 14 (= 2 weeks in days)
 | 
			
		||||
            # - there are max 3 "weeks on two" per month
 | 
			
		||||
            if (date - self.date).days % 14:
 | 
			
		||||
                date += tz.timedelta(days=7)
 | 
			
		||||
            dates = (date + tz.timedelta(days=14 * i) for i in range(0, 3))
 | 
			
		||||
        else:
 | 
			
		||||
            dates = (
 | 
			
		||||
                date + tz.timedelta(days=7 * week)
 | 
			
		||||
                for week in range(0, 5)
 | 
			
		||||
                if freq & (0b1 << week)
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        return [self.normalize(date) for date in dates if date.month == month]
 | 
			
		||||
 | 
			
		||||
    def diffusions_of_month(self, date):
 | 
			
		||||
        """Get episodes and diffusions for month of provided date, including
 | 
			
		||||
        reruns.
 | 
			
		||||
 | 
			
		||||
        :returns: tuple([Episode], [Diffusion])
 | 
			
		||||
        """
 | 
			
		||||
        from .diffusion import Diffusion
 | 
			
		||||
        from .episode import Episode
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            self.initial is not None
 | 
			
		||||
            or self.frequency == Schedule.Frequency.ponctual
 | 
			
		||||
        ):
 | 
			
		||||
            return [], []
 | 
			
		||||
 | 
			
		||||
        # dates for self and reruns as (date, initial)
 | 
			
		||||
        reruns = [
 | 
			
		||||
            (rerun, rerun.date - self.date) for rerun in self.rerun_set.all()
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        dates = {date: None for date in self.dates_of_month(date)}
 | 
			
		||||
        dates.update(
 | 
			
		||||
            (rerun.normalize(date.date() + delta), date)
 | 
			
		||||
            for date in list(dates.keys())
 | 
			
		||||
            for rerun, delta in reruns
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # remove dates corresponding to existing diffusions
 | 
			
		||||
        saved = set(
 | 
			
		||||
            Diffusion.objects.filter(
 | 
			
		||||
                start__in=dates.keys(), program=self.program, schedule=self
 | 
			
		||||
            ).values_list("start", flat=True)
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # make diffs
 | 
			
		||||
        duration = utils.to_timedelta(self.duration)
 | 
			
		||||
        diffusions = {}
 | 
			
		||||
        episodes = {}
 | 
			
		||||
 | 
			
		||||
        for date, initial in dates.items():
 | 
			
		||||
            if date in saved:
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if initial is None:
 | 
			
		||||
                episode = Episode.from_page(self.program, date=date)
 | 
			
		||||
                episode.date = date
 | 
			
		||||
                episodes[date] = episode
 | 
			
		||||
            else:
 | 
			
		||||
                episode = episodes[initial]
 | 
			
		||||
                initial = diffusions[initial]
 | 
			
		||||
 | 
			
		||||
            diffusions[date] = Diffusion(
 | 
			
		||||
                episode=episode,
 | 
			
		||||
                schedule=self,
 | 
			
		||||
                type=Diffusion.TYPE_ON_AIR,
 | 
			
		||||
                initial=initial,
 | 
			
		||||
                start=date,
 | 
			
		||||
                end=date + duration,
 | 
			
		||||
            )
 | 
			
		||||
        return episodes.values(), diffusions.values()
 | 
			
		||||
@ -6,9 +6,11 @@ from django.utils import timezone as tz
 | 
			
		||||
 | 
			
		||||
from aircox import utils
 | 
			
		||||
from aircox.conf import settings
 | 
			
		||||
from .episode import Episode, Diffusion
 | 
			
		||||
from .diffusion import Diffusion
 | 
			
		||||
from .episode import Episode
 | 
			
		||||
from .page import Page
 | 
			
		||||
from .program import Program, Schedule
 | 
			
		||||
from .program import Program
 | 
			
		||||
from .schedule import Schedule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Add a default group to a user when it is created. It also assigns a list
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,7 @@
 | 
			
		||||
<section>
 | 
			
		||||
    <h4 class="title is-4">{% translate "Diffusions" %}</h4>
 | 
			
		||||
    {% for schedule in program.schedule_set.all %}
 | 
			
		||||
    {{ schedule.get_frequency_verbose }}
 | 
			
		||||
    {{ schedule.get_frequency_display }}
 | 
			
		||||
    {% with schedule.start|date:"H:i" as start %}
 | 
			
		||||
    {% with schedule.end|date:"H:i" as end %}
 | 
			
		||||
    <time datetime="{{ start }}">{{ start }}</time>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										72
									
								
								aircox/tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								aircox/tests/conftest.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
from datetime import time, timedelta
 | 
			
		||||
import itertools
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from model_bakery import baker
 | 
			
		||||
 | 
			
		||||
from aircox import models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def stations():
 | 
			
		||||
    return baker.make("aircox.station", _quantity=2)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def programs(stations):
 | 
			
		||||
    items = list(
 | 
			
		||||
        itertools.chain(
 | 
			
		||||
            *(
 | 
			
		||||
                baker.make("aircox.program", station=station, _quantity=3)
 | 
			
		||||
                for station in stations
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    for item in items:
 | 
			
		||||
        item.save()
 | 
			
		||||
    return items
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def sched_initials(programs):
 | 
			
		||||
    # use concrete class; timezone is provided in order to ensure DST
 | 
			
		||||
    items = [
 | 
			
		||||
        baker.prepare(
 | 
			
		||||
            "aircox.schedule",
 | 
			
		||||
            program=program,
 | 
			
		||||
            time=time(16, 00),
 | 
			
		||||
            timezone="Europe/Brussels",
 | 
			
		||||
        )
 | 
			
		||||
        for program in programs
 | 
			
		||||
    ]
 | 
			
		||||
    models.Schedule.objects.bulk_create(items)
 | 
			
		||||
    return items
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def sched_reruns(sched_initials):
 | 
			
		||||
    # use concrete class
 | 
			
		||||
    items = [
 | 
			
		||||
        baker.prepare(
 | 
			
		||||
            "aircox.schedule",
 | 
			
		||||
            initial=initial,
 | 
			
		||||
            program=initial.program,
 | 
			
		||||
            date=initial.date,
 | 
			
		||||
            time=(initial.start + timedelta(hours=1)).time(),
 | 
			
		||||
        )
 | 
			
		||||
        for initial in sched_initials
 | 
			
		||||
    ]
 | 
			
		||||
    models.Schedule.objects.bulk_create(items)
 | 
			
		||||
    return items
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def schedules(sched_initials, sched_reruns):
 | 
			
		||||
    return sched_initials + sched_reruns
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.fixture
 | 
			
		||||
def episodes(programs):
 | 
			
		||||
    return [
 | 
			
		||||
        baker.make("aircox.episode", parent=program) for program in programs
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										19
									
								
								aircox/tests/models/test_diffusion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								aircox/tests/models/test_diffusion.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestDiffusionQuerySet:
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_episode_by_obj(self, episodes):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_episode_by_id(self, episodes):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_on_air(self, episodes):
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_now(self, episodes):
 | 
			
		||||
        pass
 | 
			
		||||
							
								
								
									
										117
									
								
								aircox/tests/models/test_rerun.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								aircox/tests/models/test_rerun.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,117 @@
 | 
			
		||||
from datetime import timedelta
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from django.core.exceptions import ValidationError
 | 
			
		||||
 | 
			
		||||
# we use Schedule as concrete class (Rerun is abstract)
 | 
			
		||||
from aircox.models import Schedule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestRerunQuerySet:
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_station_by_obj(self, stations, schedules):
 | 
			
		||||
        for station in stations:
 | 
			
		||||
            queryset = (
 | 
			
		||||
                Schedule.objects.station(station)
 | 
			
		||||
                .distinct()
 | 
			
		||||
                .values_list("program__station", flat=True)
 | 
			
		||||
            )
 | 
			
		||||
            assert queryset.count() == 1
 | 
			
		||||
            assert queryset.first() == station.pk
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_station_by_id(self, stations, schedules):
 | 
			
		||||
        for station in stations:
 | 
			
		||||
            queryset = (
 | 
			
		||||
                Schedule.objects.station(id=station.pk)
 | 
			
		||||
                .distinct()
 | 
			
		||||
                .values_list("program__station", flat=True)
 | 
			
		||||
            )
 | 
			
		||||
            assert queryset.count() == 1
 | 
			
		||||
            assert queryset.first() == station.pk
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_program_by_obj(self, programs, schedules):
 | 
			
		||||
        for program in programs:
 | 
			
		||||
            queryset = (
 | 
			
		||||
                Schedule.objects.program(program)
 | 
			
		||||
                .distinct()
 | 
			
		||||
                .values_list("program", flat=True)
 | 
			
		||||
            )
 | 
			
		||||
            assert queryset.count() == 1
 | 
			
		||||
            assert queryset.first() == program.pk
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_program_by_id(self, programs, schedules):
 | 
			
		||||
        for program in programs:
 | 
			
		||||
            queryset = (
 | 
			
		||||
                Schedule.objects.program(id=program.pk)
 | 
			
		||||
                .distinct()
 | 
			
		||||
                .values_list("program", flat=True)
 | 
			
		||||
            )
 | 
			
		||||
            assert queryset.count() == 1
 | 
			
		||||
            assert queryset.first() == program.pk
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_rerun(self, schedules):
 | 
			
		||||
        queryset = Schedule.objects.rerun().values_list("initial", flat=True)
 | 
			
		||||
        assert None not in queryset
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_initial(self, schedules):
 | 
			
		||||
        queryset = (
 | 
			
		||||
            Schedule.objects.initial()
 | 
			
		||||
            .distinct()
 | 
			
		||||
            .values_list("initial", flat=True)
 | 
			
		||||
        )
 | 
			
		||||
        assert queryset.count() == 1
 | 
			
		||||
        assert queryset.first() is None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestRerun:
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_is_initial_true(self, sched_initials):
 | 
			
		||||
        assert all(r.is_initial for r in sched_initials)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_is_initial_false(self, sched_reruns):
 | 
			
		||||
        assert all(not r.is_initial for r in sched_reruns)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_is_rerun_true(self, sched_reruns):
 | 
			
		||||
        assert all(r.is_rerun for r in sched_reruns)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_is_rerun_false(self, sched_initials):
 | 
			
		||||
        assert all(not r.is_rerun for r in sched_initials)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_get_initial_of_initials(self, sched_initials):
 | 
			
		||||
        assert all(r.get_initial() is r for r in sched_initials)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_get_initial_of_reruns(self, sched_reruns):
 | 
			
		||||
        assert all(r.get_initial() is r.initial for r in sched_reruns)
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_clean_success(self, sched_reruns):
 | 
			
		||||
        for rerun in sched_reruns:
 | 
			
		||||
            rerun.clean()
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_clean_fails(self, sched_reruns):
 | 
			
		||||
        for rerun in sched_reruns:
 | 
			
		||||
            rerun.time = (rerun.initial.start - timedelta(hours=2)).time()
 | 
			
		||||
            with pytest.raises(ValidationError):
 | 
			
		||||
                rerun.clean()
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_save_rerun(self, sched_reruns):
 | 
			
		||||
        for rerun in sched_reruns:
 | 
			
		||||
            rerun.program = None
 | 
			
		||||
            rerun.save_rerun()
 | 
			
		||||
            assert rerun.program == rerun.initial.program
 | 
			
		||||
 | 
			
		||||
    # TODO: save()
 | 
			
		||||
    # save_initial is empty, thus not tested
 | 
			
		||||
							
								
								
									
										135
									
								
								aircox/tests/models/test_schedule.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								aircox/tests/models/test_schedule.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
			
		||||
from datetime import date, datetime, time, timedelta
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from model_bakery import baker
 | 
			
		||||
 | 
			
		||||
import calendar
 | 
			
		||||
from dateutil.relativedelta import relativedelta
 | 
			
		||||
 | 
			
		||||
from aircox import utils
 | 
			
		||||
from aircox.models import Diffusion, Schedule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestSchedule:
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_save_rerun(self, sched_reruns):
 | 
			
		||||
        for schedule in sched_reruns:
 | 
			
		||||
            schedule.duration = None
 | 
			
		||||
            schedule.frequency = None
 | 
			
		||||
            schedule.save_rerun()
 | 
			
		||||
            assert schedule.program == schedule.initial.program
 | 
			
		||||
            assert schedule.duration == schedule.initial.duration
 | 
			
		||||
            assert schedule.frequency == schedule.initial.frequency
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_tz(self, schedules):
 | 
			
		||||
        for schedule in schedules:
 | 
			
		||||
            assert schedule.timezone == schedule.tz.zone
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_start(self, schedules):
 | 
			
		||||
        for schedule in schedules:
 | 
			
		||||
            assert schedule.start.date() == schedule.date
 | 
			
		||||
            assert schedule.start.time() == schedule.time
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_end(self, schedules):
 | 
			
		||||
        for schedule in schedules:
 | 
			
		||||
            delta = utils.to_timedelta(schedule.duration)
 | 
			
		||||
            assert schedule.end - schedule.start == delta
 | 
			
		||||
 | 
			
		||||
    # def test_get_frequency_display(self):
 | 
			
		||||
    #    pass
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_normalize(self, schedules):
 | 
			
		||||
        for schedule in schedules:
 | 
			
		||||
            dt = datetime.combine(schedule.date, schedule.time)
 | 
			
		||||
            assert schedule.normalize(dt).tzinfo.zone == schedule.timezone
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_dates_of_month_ponctual(self):
 | 
			
		||||
        schedule = baker.prepare(
 | 
			
		||||
            Schedule, frequency=Schedule.Frequency.ponctual
 | 
			
		||||
        )
 | 
			
		||||
        at = schedule.date + relativedelta(months=4)
 | 
			
		||||
        assert schedule.dates_of_month(at) == []
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    @pytest.mark.parametrize("months", range(0, 25, 2))
 | 
			
		||||
    @pytest.mark.parametrize("hour", range(0, 24, 3))
 | 
			
		||||
    def test_dates_of_month_last(self, months, hour):
 | 
			
		||||
        schedule = baker.prepare(
 | 
			
		||||
            Schedule, time=time(hour, 00), frequency=Schedule.Frequency.last
 | 
			
		||||
        )
 | 
			
		||||
        at = schedule.date + relativedelta(months=months)
 | 
			
		||||
        datetimes = schedule.dates_of_month(at)
 | 
			
		||||
        assert len(datetimes) == 1
 | 
			
		||||
 | 
			
		||||
        dt = datetimes[0]
 | 
			
		||||
        self._assert_date(schedule, at, dt)
 | 
			
		||||
 | 
			
		||||
        month_info = calendar.monthrange(at.year, at.month)
 | 
			
		||||
        at = date(at.year, at.month, month_info[1])
 | 
			
		||||
        if at.weekday() < schedule.date.weekday():
 | 
			
		||||
            at -= timedelta(days=7)
 | 
			
		||||
        at += timedelta(days=schedule.date.weekday()) - timedelta(
 | 
			
		||||
            days=at.weekday()
 | 
			
		||||
        )
 | 
			
		||||
        assert dt.date() == at
 | 
			
		||||
 | 
			
		||||
    # since the same method is used for first, second, etc. frequencies
 | 
			
		||||
    # we assume testing every is sufficient
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    @pytest.mark.parametrize("months", range(0, 25, 2))
 | 
			
		||||
    @pytest.mark.parametrize("hour", range(0, 24, 3))
 | 
			
		||||
    def test_dates_of_month_every(self, months, hour):
 | 
			
		||||
        schedule = baker.prepare(
 | 
			
		||||
            Schedule, time=time(hour, 00), frequency=Schedule.Frequency.every
 | 
			
		||||
        )
 | 
			
		||||
        at = schedule.date + relativedelta(months=months)
 | 
			
		||||
        datetimes = schedule.dates_of_month(at)
 | 
			
		||||
        last = None
 | 
			
		||||
        for dt in datetimes:
 | 
			
		||||
            self._assert_date(schedule, at, dt)
 | 
			
		||||
            if last:
 | 
			
		||||
                assert (dt - last).days == 7
 | 
			
		||||
            last = dt
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    @pytest.mark.parametrize("months", range(0, 25, 2))
 | 
			
		||||
    @pytest.mark.parametrize("hour", range(0, 24, 3))
 | 
			
		||||
    def test_dates_of_month_one_on_two(self, months, hour):
 | 
			
		||||
        schedule = baker.prepare(
 | 
			
		||||
            Schedule,
 | 
			
		||||
            time=time(hour, 00),
 | 
			
		||||
            frequency=Schedule.Frequency.one_on_two,
 | 
			
		||||
        )
 | 
			
		||||
        at = schedule.date + relativedelta(months=months)
 | 
			
		||||
        datetimes = schedule.dates_of_month(at)
 | 
			
		||||
        for dt in datetimes:
 | 
			
		||||
            self._assert_date(schedule, at, dt)
 | 
			
		||||
            delta = dt.date() - schedule.date
 | 
			
		||||
            assert delta.days % 14 == 0
 | 
			
		||||
 | 
			
		||||
    def _assert_date(self, schedule, at, dt):
 | 
			
		||||
        assert dt.year == at.year
 | 
			
		||||
        assert dt.month == at.month
 | 
			
		||||
        assert dt.weekday() == schedule.date.weekday()
 | 
			
		||||
        assert dt.time() == schedule.time
 | 
			
		||||
        assert dt.tzinfo.zone == schedule.timezone
 | 
			
		||||
 | 
			
		||||
    @pytest.mark.django_db
 | 
			
		||||
    def test_diffusions_of_month(self, sched_initials):
 | 
			
		||||
        # TODO: test values of initial, rerun
 | 
			
		||||
        for schedule in sched_initials:
 | 
			
		||||
            at = schedule.date + timedelta(days=30)
 | 
			
		||||
            dates = set(schedule.dates_of_month(at))
 | 
			
		||||
            episodes, diffusions = schedule.diffusions_of_month(at)
 | 
			
		||||
 | 
			
		||||
            assert all(r.date in dates for r in episodes)
 | 
			
		||||
            assert all(
 | 
			
		||||
                (not r.initial or r.date in dates)
 | 
			
		||||
                and r.type == Diffusion.TYPE_ON_AIR
 | 
			
		||||
                for r in diffusions
 | 
			
		||||
            )
 | 
			
		||||
@ -1,66 +0,0 @@
 | 
			
		||||
import calendar
 | 
			
		||||
import datetime
 | 
			
		||||
import logging
 | 
			
		||||
 | 
			
		||||
from dateutil.relativedelta import relativedelta
 | 
			
		||||
from django.test import TestCase
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
 | 
			
		||||
from aircox.models import Schedule
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger("aircox.test")
 | 
			
		||||
logger.setLevel("INFO")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleCheck(TestCase):
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        self.schedules = [
 | 
			
		||||
            Schedule(
 | 
			
		||||
                date=tz.now(),
 | 
			
		||||
                duration=datetime.time(1, 30),
 | 
			
		||||
                frequency=frequency,
 | 
			
		||||
            )
 | 
			
		||||
            for frequency in Schedule.Frequency.__members__.values()
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def test_frequencies(self):
 | 
			
		||||
        for schedule in self.schedules:
 | 
			
		||||
            logger.info(
 | 
			
		||||
                "- test frequency %s" % schedule.get_frequency_display()
 | 
			
		||||
            )
 | 
			
		||||
            date = schedule.date
 | 
			
		||||
            count = 24
 | 
			
		||||
            while count:
 | 
			
		||||
                logger.info(
 | 
			
		||||
                    "- month %(month)s/%(year)s"
 | 
			
		||||
                    % {"month": date.month, "year": date.year}
 | 
			
		||||
                )
 | 
			
		||||
                count -= 1
 | 
			
		||||
                dates = schedule.dates_of_month(date)
 | 
			
		||||
                if schedule.frequency == schedule.Frequency.one_on_two:
 | 
			
		||||
                    self.check_one_on_two(schedule, date, dates)
 | 
			
		||||
                elif schedule.frequency == schedule.Frequency.last:
 | 
			
		||||
                    self.check_last(schedule, date, dates)
 | 
			
		||||
                else:
 | 
			
		||||
                    pass
 | 
			
		||||
                date += relativedelta(months=1)
 | 
			
		||||
 | 
			
		||||
    def check_one_on_two(self, schedule, date, dates):
 | 
			
		||||
        for date in dates:
 | 
			
		||||
            delta = date.date() - schedule.date.date()
 | 
			
		||||
            self.assertEqual(delta.days % 14, 0)
 | 
			
		||||
 | 
			
		||||
    def check_last(self, schedule, date, dates):
 | 
			
		||||
        month_info = calendar.monthrange(date.year, date.month)
 | 
			
		||||
        date = datetime.date(date.year, date.month, month_info[1])
 | 
			
		||||
 | 
			
		||||
        # end of month before the wanted weekday: move one week back
 | 
			
		||||
        if date.weekday() < schedule.date.weekday():
 | 
			
		||||
            date -= datetime.timedelta(days=7)
 | 
			
		||||
 | 
			
		||||
        date -= datetime.timedelta(days=date.weekday())
 | 
			
		||||
        date += datetime.timedelta(days=schedule.date.weekday())
 | 
			
		||||
        self.assertEqual(date, dates[0].date())
 | 
			
		||||
 | 
			
		||||
    def check_n_of_week(self, schedule, date, dates):
 | 
			
		||||
        pass
 | 
			
		||||
@ -1,7 +1,8 @@
 | 
			
		||||
from . import admin
 | 
			
		||||
from .article import ArticleDetailView, ArticleListView
 | 
			
		||||
from .base import BaseAPIView, BaseView
 | 
			
		||||
from .episode import DiffusionListView, EpisodeDetailView, EpisodeListView
 | 
			
		||||
from .diffusion import DiffusionListView
 | 
			
		||||
from .episode import EpisodeDetailView, EpisodeListView
 | 
			
		||||
from .home import HomeView
 | 
			
		||||
from .log import LogListAPIView, LogListView
 | 
			
		||||
from .page import (
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								aircox/views/diffusion.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								aircox/views/diffusion.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.views.generic import ListView
 | 
			
		||||
 | 
			
		||||
from aircox.models import Diffusion, StaticPage
 | 
			
		||||
from .base import BaseView
 | 
			
		||||
from .mixins import AttachedToMixin, GetDateMixin
 | 
			
		||||
 | 
			
		||||
__all__ = ("DiffusionListView",)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
 | 
			
		||||
    """View for timetables."""
 | 
			
		||||
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    has_filters = True
 | 
			
		||||
    redirect_date_url = "diffusion-list"
 | 
			
		||||
    attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS
 | 
			
		||||
 | 
			
		||||
    def get_date(self):
 | 
			
		||||
        date = super().get_date()
 | 
			
		||||
        return date if date is not None else datetime.date.today()
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return super().get_queryset().date(self.date).order_by("start")
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        start = self.date - datetime.timedelta(days=self.date.weekday())
 | 
			
		||||
        dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
 | 
			
		||||
        return super().get_context_data(date=self.date, dates=dates, **kwargs)
 | 
			
		||||
@ -1,18 +1,11 @@
 | 
			
		||||
import datetime
 | 
			
		||||
 | 
			
		||||
from django.views.generic import ListView
 | 
			
		||||
 | 
			
		||||
from ..filters import EpisodeFilters
 | 
			
		||||
from ..models import Diffusion, Episode, Program, StaticPage
 | 
			
		||||
from .base import BaseView
 | 
			
		||||
from .mixins import AttachedToMixin, GetDateMixin
 | 
			
		||||
from ..models import Episode, Program, StaticPage
 | 
			
		||||
from .page import PageListView
 | 
			
		||||
from .program import ProgramPageDetailView
 | 
			
		||||
 | 
			
		||||
__all__ = (
 | 
			
		||||
    "EpisodeDetailView",
 | 
			
		||||
    "EpisodeListView",
 | 
			
		||||
    "DiffusionListView",
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -32,24 +25,3 @@ class EpisodeListView(PageListView):
 | 
			
		||||
    has_headline = True
 | 
			
		||||
    parent_model = Program
 | 
			
		||||
    attach_to_value = StaticPage.ATTACH_TO_EPISODES
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionListView(GetDateMixin, AttachedToMixin, BaseView, ListView):
 | 
			
		||||
    """View for timetables."""
 | 
			
		||||
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    has_filters = True
 | 
			
		||||
    redirect_date_url = "diffusion-list"
 | 
			
		||||
    attach_to_value = StaticPage.ATTACH_TO_DIFFUSIONS
 | 
			
		||||
 | 
			
		||||
    def get_date(self):
 | 
			
		||||
        date = super().get_date()
 | 
			
		||||
        return date if date is not None else datetime.date.today()
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        return super().get_queryset().date(self.date).order_by("start")
 | 
			
		||||
 | 
			
		||||
    def get_context_data(self, **kwargs):
 | 
			
		||||
        start = self.date - datetime.timedelta(days=self.date.weekday())
 | 
			
		||||
        dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
 | 
			
		||||
        return super().get_context_data(date=self.date, dates=dates, **kwargs)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user