#93: reorganise Rerun, Diffusion, Schedule module #95
@ -16,7 +16,7 @@ repos:
 | 
				
			|||||||
  rev: 6.0.0
 | 
					  rev: 6.0.0
 | 
				
			||||||
  hooks:
 | 
					  hooks:
 | 
				
			||||||
    - id: flake8
 | 
					    - id: flake8
 | 
				
			||||||
      exclude: instance/settings/
 | 
					      exclude: ^instance/settings/|migrations/
 | 
				
			||||||
- repo: https://github.com/PyCQA/docformatter.git
 | 
					- repo: https://github.com/PyCQA/docformatter.git
 | 
				
			||||||
  rev: v1.5.1
 | 
					  rev: v1.5.1
 | 
				
			||||||
  hooks:
 | 
					  hooks:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,9 +1,11 @@
 | 
				
			|||||||
from . import filters
 | 
					from . import filters
 | 
				
			||||||
from .article import ArticleAdmin
 | 
					from .article import ArticleAdmin
 | 
				
			||||||
from .episode import DiffusionAdmin, EpisodeAdmin
 | 
					from .diffusion import DiffusionAdmin
 | 
				
			||||||
 | 
					from .episode import EpisodeAdmin
 | 
				
			||||||
from .log import LogAdmin
 | 
					from .log import LogAdmin
 | 
				
			||||||
from .page import PageAdmin, StaticPageAdmin
 | 
					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 .sound import SoundAdmin, TrackAdmin
 | 
				
			||||||
from .station import StationAdmin
 | 
					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 adminsortable2.admin import SortableAdminBase
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from django.forms import ModelForm
 | 
					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 .page import PageAdmin
 | 
				
			||||||
from .sound import SoundInline, TrackInline
 | 
					from .sound import SoundInline, TrackInline
 | 
				
			||||||
 | 
					from .diffusion import 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")
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EpisodeAdminForm(ModelForm):
 | 
					class EpisodeAdminForm(ModelForm):
 | 
				
			||||||
 | 
				
			|||||||
@ -1,26 +1,12 @@
 | 
				
			|||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from django.forms import ModelForm
 | 
					 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					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 .page import PageAdmin
 | 
				
			||||||
 | 
					from .schedule import ScheduleInline
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# In order to simplify schedule_post_save algorithm, an existing schedule can't
 | 
					__all__ = ("ProgramAdmin", "StreamInline", "StreamAdmin")
 | 
				
			||||||
# 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
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StreamInline(admin.TabularInline):
 | 
					class StreamInline(admin.TabularInline):
 | 
				
			||||||
@ -58,36 +44,6 @@ class ProgramAdmin(PageAdmin):
 | 
				
			|||||||
        return fields
 | 
					        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)
 | 
					@admin.register(Stream)
 | 
				
			||||||
class StreamAdmin(admin.ModelAdmin):
 | 
					class StreamAdmin(admin.ModelAdmin):
 | 
				
			||||||
    list_display = ("id", "program", "delay", "begin", "end")
 | 
					    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_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 []
 | 
				
			||||||
							
								
								
									
										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 . import signals
 | 
				
			||||||
from .article import Article
 | 
					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 .log import Log, LogArchiver, LogQuerySet
 | 
				
			||||||
from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
 | 
					from .page import Category, Comment, NavItem, Page, PageQuerySet, StaticPage
 | 
				
			||||||
from .program import (
 | 
					from .program import Program, ProgramChildQuerySet, ProgramQuerySet, Stream
 | 
				
			||||||
    BaseRerun,
 | 
					from .schedule import Schedule
 | 
				
			||||||
    BaseRerunQuerySet,
 | 
					 | 
				
			||||||
    Program,
 | 
					 | 
				
			||||||
    ProgramChildQuerySet,
 | 
					 | 
				
			||||||
    ProgramQuerySet,
 | 
					 | 
				
			||||||
    Schedule,
 | 
					 | 
				
			||||||
    Stream,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from .sound import Sound, SoundQuerySet, Track
 | 
					from .sound import Sound, SoundQuerySet, Track
 | 
				
			||||||
from .station import Port, Station, StationQuerySet
 | 
					from .station import Port, Station, StationQuerySet
 | 
				
			||||||
from .user_settings import UserSettings
 | 
					from .user_settings import UserSettings
 | 
				
			||||||
@ -36,8 +30,6 @@ __all__ = (
 | 
				
			|||||||
    "Stream",
 | 
					    "Stream",
 | 
				
			||||||
    "Schedule",
 | 
					    "Schedule",
 | 
				
			||||||
    "ProgramChildQuerySet",
 | 
					    "ProgramChildQuerySet",
 | 
				
			||||||
    "BaseRerun",
 | 
					 | 
				
			||||||
    "BaseRerunQuerySet",
 | 
					 | 
				
			||||||
    "Sound",
 | 
					    "Sound",
 | 
				
			||||||
    "SoundQuerySet",
 | 
					    "SoundQuerySet",
 | 
				
			||||||
    "Track",
 | 
					    "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
 | 
				
			||||||
 | 
					        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),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
@ -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.functional import cached_property
 | 
				
			||||||
from django.utils.translation import gettext_lazy as _
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
from easy_thumbnails.files import get_thumbnailer
 | 
					from easy_thumbnails.files import get_thumbnailer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aircox.conf import settings
 | 
					from aircox.conf import settings
 | 
				
			||||||
from aircox import utils
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .page import Page
 | 
					from .page import Page
 | 
				
			||||||
from .program import (
 | 
					from .program import ProgramChildQuerySet
 | 
				
			||||||
    BaseRerun,
 | 
					 | 
				
			||||||
    BaseRerunQuerySet,
 | 
					 | 
				
			||||||
    ProgramChildQuerySet,
 | 
					 | 
				
			||||||
    Schedule,
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ("Episode", "Diffusion", "DiffusionQuerySet")
 | 
					__all__ = ("Episode",)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Episode(Page):
 | 
					class Episode(Page):
 | 
				
			||||||
@ -90,269 +79,3 @@ class Episode(Page):
 | 
				
			|||||||
        return super().get_init_kwargs_from(
 | 
					        return super().get_init_kwargs_from(
 | 
				
			||||||
            page, title=title, program=page, **kwargs
 | 
					            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
 | 
					from aircox.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = ("Settings", "settings")
 | 
					__all__ = ("Settings", "settings")
 | 
				
			||||||
from .episode import Diffusion
 | 
					from .diffusion import Diffusion
 | 
				
			||||||
from .sound import Sound, Track
 | 
					from .sound import Sound, Track
 | 
				
			||||||
from .station import Station
 | 
					from .station import Station
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,21 +1,13 @@
 | 
				
			|||||||
import calendar
 | 
					 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import shutil
 | 
					import shutil
 | 
				
			||||||
from collections import OrderedDict
 | 
					 | 
				
			||||||
from enum import IntEnum
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pytz
 | 
					 | 
				
			||||||
from django.conf import settings as conf
 | 
					from django.conf import settings as conf
 | 
				
			||||||
from django.core.exceptions import ValidationError
 | 
					 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
from django.db.models import F
 | 
					from django.db.models import F
 | 
				
			||||||
from django.db.models.functions import Concat, Substr
 | 
					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 django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from aircox import utils
 | 
					 | 
				
			||||||
from aircox.conf import settings
 | 
					from aircox.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from .page import Page, PageQuerySet
 | 
					from .page import Page, PageQuerySet
 | 
				
			||||||
@ -26,12 +18,9 @@ logger = logging.getLogger("aircox")
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
__all__ = (
 | 
					__all__ = (
 | 
				
			||||||
    "Program",
 | 
					    "Program",
 | 
				
			||||||
 | 
					    "ProgramChildQuerySet",
 | 
				
			||||||
    "ProgramQuerySet",
 | 
					    "ProgramQuerySet",
 | 
				
			||||||
    "Stream",
 | 
					    "Stream",
 | 
				
			||||||
    "Schedule",
 | 
					 | 
				
			||||||
    "ProgramChildQuerySet",
 | 
					 | 
				
			||||||
    "BaseRerun",
 | 
					 | 
				
			||||||
    "BaseRerunQuerySet",
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -167,352 +156,6 @@ class ProgramChildQuerySet(PageQuerySet):
 | 
				
			|||||||
        return self.parent(program, id)
 | 
					        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):
 | 
					class Stream(models.Model):
 | 
				
			||||||
    """When there are no program scheduled, it is possible to play sounds in
 | 
					    """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
 | 
					    order to avoid blanks. A Stream is a Program that plays this role, and
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										102
									
								
								aircox/models/rerun.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								aircox/models/rerun.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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")}
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
							
								
								
									
										254
									
								
								aircox/models/schedule.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								aircox/models/schedule.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,254 @@
 | 
				
			|||||||
 | 
					import calendar
 | 
				
			||||||
 | 
					from collections import OrderedDict
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.IntegerChoice):
 | 
				
			||||||
 | 
					        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=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=Frequency.choices,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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 .diffusion 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 .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 = 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")
 | 
				
			||||||
@ -6,9 +6,11 @@ from django.utils import timezone as tz
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from aircox import utils
 | 
					from aircox import utils
 | 
				
			||||||
from aircox.conf import settings
 | 
					from aircox.conf import settings
 | 
				
			||||||
from .episode import Episode, Diffusion
 | 
					from .diffusion import Diffusion
 | 
				
			||||||
 | 
					from .episode import Episode
 | 
				
			||||||
from .page import Page
 | 
					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
 | 
					# Add a default group to a user when it is created. It also assigns a list
 | 
				
			||||||
 | 
				
			|||||||
@ -1,7 +1,8 @@
 | 
				
			|||||||
from . import admin
 | 
					from . import admin
 | 
				
			||||||
from .article import ArticleDetailView, ArticleListView
 | 
					from .article import ArticleDetailView, ArticleListView
 | 
				
			||||||
from .base import BaseAPIView, BaseView
 | 
					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 .home import HomeView
 | 
				
			||||||
from .log import LogListAPIView, LogListView
 | 
					from .log import LogListAPIView, LogListView
 | 
				
			||||||
from .page import (
 | 
					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 ..filters import EpisodeFilters
 | 
				
			||||||
from ..models import Diffusion, Episode, Program, StaticPage
 | 
					from ..models import Episode, Program, StaticPage
 | 
				
			||||||
from .base import BaseView
 | 
					 | 
				
			||||||
from .mixins import AttachedToMixin, GetDateMixin
 | 
					 | 
				
			||||||
from .page import PageListView
 | 
					from .page import PageListView
 | 
				
			||||||
from .program import ProgramPageDetailView
 | 
					from .program import ProgramPageDetailView
 | 
				
			||||||
 | 
					
 | 
				
			||||||
__all__ = (
 | 
					__all__ = (
 | 
				
			||||||
    "EpisodeDetailView",
 | 
					    "EpisodeDetailView",
 | 
				
			||||||
    "EpisodeListView",
 | 
					    "EpisodeListView",
 | 
				
			||||||
    "DiffusionListView",
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -32,24 +25,3 @@ class EpisodeListView(PageListView):
 | 
				
			|||||||
    has_headline = True
 | 
					    has_headline = True
 | 
				
			||||||
    parent_model = Program
 | 
					    parent_model = Program
 | 
				
			||||||
    attach_to_value = StaticPage.ATTACH_TO_EPISODES
 | 
					    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