new management tool: add
This commit is contained in:
parent
57f9159dbb
commit
f7d36467f0
|
@ -81,6 +81,7 @@ class SoundFileAdmin (MetadataAdmin):
|
|||
]
|
||||
|
||||
#inlines = [ EpisodeInline ]
|
||||
inlines = [ EventInline ]
|
||||
|
||||
|
||||
|
||||
|
@ -93,16 +94,15 @@ class ArticleAdmin (PublicationAdmin):
|
|||
|
||||
class ProgramAdmin (PublicationAdmin):
|
||||
fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
|
||||
prepopulated_fields = { 'tag': ('title',) }
|
||||
inlines = [ EpisodeInline, ScheduleInline ]
|
||||
|
||||
fieldsets[1][1]['fields'] += ['email', 'url', 'tag']
|
||||
fieldsets[1][1]['fields'] += ['email', 'url', 'non_stop']
|
||||
|
||||
|
||||
|
||||
class EpisodeAdmin (PublicationAdmin):
|
||||
fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
|
||||
inlines = [ EventInline, SoundFileInline ]
|
||||
inlines = [ SoundFileInline ]
|
||||
list_filter = ['parent'] + PublicationAdmin.list_filter
|
||||
|
||||
fieldsets[0][1]['fields'] += ['tracks']
|
||||
|
|
143
programs/management/commands/add.py
Normal file
143
programs/management/commands/add.py
Normal file
|
@ -0,0 +1,143 @@
|
|||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils import timezone
|
||||
import programs.models as models
|
||||
|
||||
|
||||
class Model:
|
||||
# dict: key is the argument name, value is the constructor
|
||||
required = {}
|
||||
optional = {}
|
||||
model = None
|
||||
|
||||
|
||||
def __init__ (self, model, required = {}, optional = {}, post = None):
|
||||
self.model = model
|
||||
self.required = required
|
||||
self.optional = optional
|
||||
self.post = post
|
||||
|
||||
|
||||
def check_or_raise (self, options):
|
||||
for req in self.required:
|
||||
if req not in options:
|
||||
raise ValueError('required argument ' + req + ' is missing')
|
||||
|
||||
|
||||
def get_kargs (self, options):
|
||||
kargs = {}
|
||||
|
||||
for i in self.required:
|
||||
if options.get(i):
|
||||
fn = self.required[i]
|
||||
kargs[i] = fn(options[i])
|
||||
|
||||
for i in self.optional:
|
||||
if options.get(i):
|
||||
print(i, options)
|
||||
fn = self.optional[i]
|
||||
kargs[i] = fn(options[i])
|
||||
|
||||
return kargs
|
||||
|
||||
|
||||
def make (self, options):
|
||||
self.check_or_raise(options)
|
||||
|
||||
kargs = self.get_kargs(options)
|
||||
instance = self.model(**kargs)
|
||||
instance.save()
|
||||
|
||||
if self.post:
|
||||
self.post(instance, options)
|
||||
|
||||
print(instance.__dict__)
|
||||
|
||||
|
||||
def DateTime (string):
|
||||
dt = timezone.datetime.strptime(string, '%Y-%m-%d %H:%M:%S')
|
||||
return timezone.make_aware(dt, timezone.get_current_timezone())
|
||||
|
||||
|
||||
def Time (string):
|
||||
dt = timezone.datetime.strptime(string, '%H:%M')
|
||||
return timezone.datetime.time(dt)
|
||||
|
||||
|
||||
|
||||
def AddTags (instance, options):
|
||||
if options.get('tags'):
|
||||
instance.tags.add(*options['tags'])
|
||||
|
||||
|
||||
models = {
|
||||
'program': Model( models.Program
|
||||
, { 'title': str }
|
||||
, { 'subtitle': str, 'can_comment': bool, 'date': DateTime
|
||||
, 'parent_id': int, 'public': bool
|
||||
, 'url': str, 'email': str, 'non_stop': bool
|
||||
}
|
||||
, AddTags
|
||||
)
|
||||
, 'article': Model( models.Article
|
||||
, { 'title': str }
|
||||
, { 'subtitle': str, 'can_comment': bool, 'date': DateTime
|
||||
, 'parent_id': int, 'public': bool
|
||||
, 'static_page': bool, 'focus': bool
|
||||
}
|
||||
, AddTags
|
||||
)
|
||||
, 'schedule': Model( models.Schedule
|
||||
, { 'parent_id': int, 'date': DateTime, 'duration': Time
|
||||
, 'frequency': int }
|
||||
, { 'rerun': bool }
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
class Command (BaseCommand):
|
||||
help="Add an element of the given model"
|
||||
|
||||
|
||||
def add_arguments (self, parser):
|
||||
parser.add_argument( 'model', type=str
|
||||
, metavar="MODEL"
|
||||
, help="model to add. It must be in [schedule,program,article]")
|
||||
|
||||
# publication/generic
|
||||
parser.add_argument('--parent_id', type=str)
|
||||
parser.add_argument('--title', type=str)
|
||||
parser.add_argument('--subtitle', type=str)
|
||||
parser.add_argument('--can_comment',action='store_true')
|
||||
parser.add_argument('--public', action='store_true')
|
||||
parser.add_argument( '--date', type=str
|
||||
, help='a valid date time (Y/m/d H:m:s')
|
||||
parser.add_argument('--tags', type=str, nargs='+')
|
||||
|
||||
# program
|
||||
parser.add_argument('--url', type=str)
|
||||
parser.add_argument('--email', type=str)
|
||||
parser.add_argument('--non_stop', type=int)
|
||||
|
||||
# article
|
||||
parser.add_argument('--static_page',action='store_true')
|
||||
parser.add_argument('--focus', action='store_true')
|
||||
|
||||
# schedule
|
||||
parser.add_argument('--duration', type=str)
|
||||
parser.add_argument('--frequency', type=int)
|
||||
parser.add_argument('--rerun', action='store_true')
|
||||
|
||||
|
||||
def handle (self, *args, **options):
|
||||
model = options.get('model')
|
||||
if not model:
|
||||
return
|
||||
|
||||
model = model.lower()
|
||||
if model not in models:
|
||||
raise ValueError("model {} is not supported".format(str(model)))
|
||||
|
||||
models[model].make(options)
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import os
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
import programs.models as models
|
||||
import programs.settings
|
||||
|
||||
|
||||
class Command (BaseCommand):
|
||||
help= "Take a look at the programs directory to check on new podcasts"
|
||||
|
||||
|
||||
def handle (self, *args, **options):
|
||||
programs = models.Program.objects.filter(schedule__isnull = True)
|
||||
|
||||
for program in programs:
|
||||
self.scan(program, program.path + '/public', public = True)
|
||||
self.scan(program, program.path + '/podcasts', embed = True)
|
||||
self.scan(program, program.path + '/private')
|
||||
|
||||
|
||||
def scan (self, program, path, public = False, embed = False):
|
||||
try:
|
||||
for filename in os.listdir(path):
|
||||
long_filename = path + '/' + filename
|
||||
|
||||
# check for new sound files
|
||||
# stat the sound files
|
||||
# match sound files against episodes - if not found, create it
|
||||
# upload public podcasts to mixcloud if required
|
||||
except:
|
||||
pass
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
import datetime
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.utils import timezone, dateformat
|
||||
|
||||
import programs.models as models
|
||||
import programs.settings
|
||||
|
||||
|
||||
class Diffusion:
|
||||
ref = None
|
||||
date_start = None
|
||||
date_end = None
|
||||
|
||||
def __init__ (self, ref, date_start, date_end):
|
||||
self.ref = ref
|
||||
self.date_start = date_start
|
||||
self.date_end = date_end
|
||||
|
||||
def __lt__ (self, d):
|
||||
return self.date_start < d.date_start and \
|
||||
self.date_end < d.date_end
|
||||
|
||||
|
||||
|
||||
class Command (BaseCommand):
|
||||
help= "check sounds to diffuse"
|
||||
|
||||
diffusions = set()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.get_next_events()
|
||||
self.get_next_episodes()
|
||||
|
||||
for diffusion in self.diffusions:
|
||||
print( diffusion.ref.__str__()
|
||||
, diffusion.date_start
|
||||
, diffusion.date_end)
|
||||
|
||||
|
||||
|
||||
def get_next_episodes (self):
|
||||
schedules = models.Schedule.objects.filter()
|
||||
for schedule in schedules:
|
||||
date = schedule.next_date()
|
||||
if not date:
|
||||
continue
|
||||
|
||||
dt = datetime.timedelta( hours = schedule.duration.hour
|
||||
, minutes = schedule.duration.minute
|
||||
, seconds = schedule.duration.second )
|
||||
|
||||
ref = models.Episode.objects.filter(date = date)[:1]
|
||||
if not ref:
|
||||
ref = ( schedule.parent, )
|
||||
|
||||
diffusion = Diffusion(ref[0], date, date + dt)
|
||||
self.diffusions.add(diffusion)
|
||||
|
||||
|
||||
def get_next_events (self):
|
||||
events = models.Event.objects.filter(date_end__gt = timezone.now(),
|
||||
canceled = False) \
|
||||
.extra(order_by = ['date'])[:10]
|
||||
for event in events:
|
||||
diffusion = Diffusion(event, event.date, event.date_end)
|
||||
self.diffusions.add(diffusion)
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,45 +1,38 @@
|
|||
import datetime
|
||||
import os
|
||||
|
||||
# django
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.defaultfilters import slugify
|
||||
|
||||
from django.http import HttpResponse, Http404
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.html import strip_tags
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
# extensions
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
|
||||
import programs.settings as settings
|
||||
|
||||
|
||||
|
||||
AFrequency = {
|
||||
'ponctual': 0x000000,
|
||||
'every week': 0b001111,
|
||||
'first week': 0x000001,
|
||||
'second week': 0x000010,
|
||||
'third week': 0x000100,
|
||||
'fourth week': 0x001000,
|
||||
'first and third': 0x000101,
|
||||
'second and fourth': 0x001010,
|
||||
'one week on two': 0x010010,
|
||||
#'uneven week': 0x100000,
|
||||
# TODO 'every day': 0x110000
|
||||
Frequency = {
|
||||
'ponctual': 0b000000
|
||||
, 'every week': 0b001111
|
||||
, 'first week': 0b000001
|
||||
, 'second week': 0b000010
|
||||
, 'third week': 0b000100
|
||||
, 'fourth week': 0b001000
|
||||
, 'first and third': 0b000101
|
||||
, 'second and fourth': 0b001010
|
||||
, 'one week on two': 0b010010
|
||||
#'uneven week': 0b100000
|
||||
# TODO 'every day': 0b110000
|
||||
}
|
||||
|
||||
|
||||
|
||||
# Translators: html safe values
|
||||
ugettext_lazy('ponctual')
|
||||
ugettext_lazy('every week')
|
||||
|
@ -52,10 +45,15 @@ ugettext_lazy('second and fourth')
|
|||
ugettext_lazy('one week on two')
|
||||
|
||||
|
||||
Frequency = [ (y, x) for x,y in AFrequency.items() ]
|
||||
RFrequency = { y: x for x,y in AFrequency.items() }
|
||||
EventType = {
|
||||
'play': 0x02 # the sound is playing / planified to play
|
||||
, 'cancel': 0x03 # the sound has been canceled from grid; useful to give
|
||||
# the info to the users
|
||||
, 'stop': 0x04 # the sound has been arbitrary stopped (non-stop or not)
|
||||
, 'non-stop': 0x05 # the sound has been played as non-stop
|
||||
#, 'streaming'
|
||||
}
|
||||
|
||||
Frequency.sort(key = lambda e: e[0])
|
||||
|
||||
|
||||
class Model (models.Model):
|
||||
|
@ -106,7 +104,7 @@ class Metadata (Model):
|
|||
)
|
||||
date = models.DateTimeField(
|
||||
_('date')
|
||||
, default = datetime.datetime.now
|
||||
, default = timezone.datetime.now
|
||||
)
|
||||
public = models.BooleanField(
|
||||
_('public')
|
||||
|
@ -190,12 +188,6 @@ class Publication (Metadata):
|
|||
#
|
||||
# Instance's methods
|
||||
#
|
||||
def get_parent (self, raise_404 = False ):
|
||||
if not parent and raise_404:
|
||||
raise Http404
|
||||
return parent
|
||||
|
||||
|
||||
def get_parents ( self, order_by = "desc", include_fields = None ):
|
||||
"""
|
||||
Return an array of the parents of the item.
|
||||
|
@ -262,10 +254,9 @@ class SoundFile (Metadata):
|
|||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
file = models.FileField(
|
||||
file = models.FileField( #FIXME: filefield
|
||||
_('file')
|
||||
, upload_to = "data/tracks"
|
||||
, blank = True
|
||||
, upload_to = lambda i, f: SoundFile.__upload_path(i,f)
|
||||
)
|
||||
duration = models.TimeField(
|
||||
_('duration')
|
||||
|
@ -277,12 +268,24 @@ class SoundFile (Metadata):
|
|||
, default = False
|
||||
, help_text = _("the file has been cut")
|
||||
)
|
||||
embed = models.TextField (
|
||||
embed = models.TextField(
|
||||
_('embed HTML code from external website')
|
||||
, blank = True
|
||||
, null = True
|
||||
, help_text = _('if set, consider the sound podcastable from there')
|
||||
)
|
||||
removed = models.BooleanField(
|
||||
default = False
|
||||
, help_text = _('this sound has been removed from filesystem')
|
||||
)
|
||||
|
||||
|
||||
def __upload_path (self, filename):
|
||||
if self.parent and self.parent.parent:
|
||||
path = self.parent.parent.path
|
||||
else:
|
||||
path = settings.AIRCOX_SOUNDFILE_DEFAULT_DIR
|
||||
return os.path.join(path, filename)
|
||||
|
||||
|
||||
def __str__ (self):
|
||||
|
@ -294,29 +297,44 @@ class SoundFile (Metadata):
|
|||
verbose_name_plural = _('Sounds')
|
||||
|
||||
|
||||
|
||||
class Schedule (Model):
|
||||
parent = models.ForeignKey( 'Program', blank = True, null = True )
|
||||
date = models.DateTimeField(_('start'))
|
||||
duration = models.TimeField(_('duration'))
|
||||
frequency = models.SmallIntegerField(_('frequency'), choices = Frequency)
|
||||
duration = models.TimeField(
|
||||
_('duration')
|
||||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
frequency = models.SmallIntegerField(
|
||||
_('frequency')
|
||||
, choices = [ (y, x) for x,y in Frequency.items() ]
|
||||
)
|
||||
rerun = models.BooleanField(_('rerun'), default = False)
|
||||
|
||||
|
||||
def match_week (self, at = datetime.date.today()):
|
||||
def match_date (self, at = timezone.datetime.today()):
|
||||
"""
|
||||
Return True if the given datetime matches the schedule
|
||||
"""
|
||||
if self.date.weekday() == at.weekday() and self.match_week(date):
|
||||
return self.date.time() == at.date.time()
|
||||
return False
|
||||
|
||||
|
||||
def match_week (self, at = timezone.datetime.today()):
|
||||
"""
|
||||
Return True if the given week number matches the schedule, False
|
||||
otherwise.
|
||||
If the schedule is ponctual, return None.
|
||||
"""
|
||||
if self.frequency == AFrequency['ponctual']:
|
||||
if self.frequency == Frequency['ponctual']:
|
||||
return None
|
||||
|
||||
if self.frequency == AFrequency['one week on two']:
|
||||
if self.frequency == Frequency['one week on two']:
|
||||
week = at.isocalendar()[1]
|
||||
return (week % 2) == (self.date.isocalendar()[1] % 2)
|
||||
|
||||
first_of_month = datetime.date(at.year, at.month, 1)
|
||||
first_of_month = timezone.datetime.date(at.year, at.month, 1)
|
||||
week = at.isocalendar()[1] - first_of_month.isocalendar()[1]
|
||||
|
||||
# weeks of month
|
||||
|
@ -326,30 +344,29 @@ class Schedule (Model):
|
|||
return (self.frequency & (0b0001 << week) > 0)
|
||||
|
||||
|
||||
|
||||
def next_date (self, at = datetime.date.today()):
|
||||
if self.frequency == AFrequency['ponctual']:
|
||||
def next_date (self, at = timezone.datetime.today()):
|
||||
if self.frequency == Frequency['ponctual']:
|
||||
return None
|
||||
|
||||
# first day of the week
|
||||
date = at - datetime.timedelta( days = at.weekday() )
|
||||
date = at - timezone.timedelta( days = at.weekday() )
|
||||
|
||||
# for the next five week, we look for a matching week.
|
||||
# when found, add the number of day since de start of the
|
||||
# we need to test if the result is >= at
|
||||
for i in range(0,5):
|
||||
if self.match_week(date):
|
||||
date_ = date + datetime.timedelta( days = self.date.weekday() )
|
||||
date_ = date + timezone.timedelta( days = self.date.weekday() )
|
||||
if date_ >= at:
|
||||
# we don't want past events
|
||||
return datetime.datetime(date_.year, date_.month, date_.day,
|
||||
return timezone.datetime(date_.year, date_.month, date_.day,
|
||||
self.date.hour, self.date.minute)
|
||||
date += datetime.timedelta( days = 7 )
|
||||
date += timezone.timedelta( days = 7 )
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def next_dates (self, at = datetime.date.today(), n = 52):
|
||||
def next_dates (self, at = timezone.datetime.today(), n = 52):
|
||||
# we could have optimized this function, but since it should not
|
||||
# be use too often, we keep a more readable and easier to debug
|
||||
# solution
|
||||
|
@ -362,20 +379,20 @@ class Schedule (Model):
|
|||
if not e:
|
||||
break
|
||||
res.append(e)
|
||||
at = res[-1] + datetime.timedelta(days = 1)
|
||||
at = res[-1] + timezone.timedelta(days = 1)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def to_string(self):
|
||||
s = ugettext_lazy( RFrequency[self.frequency] )
|
||||
if self.rerun:
|
||||
return s + ' (' + _('rerun') + ')'
|
||||
return s
|
||||
|
||||
#def to_string(self):
|
||||
# s = ugettext_lazy( RFrequency[self.frequency] )
|
||||
# if self.rerun:
|
||||
# return s + ' (' + _('rerun') + ')'
|
||||
# return s
|
||||
|
||||
def __str__ (self):
|
||||
return self.parent.title + ': ' + RFrequency[self.frequency]
|
||||
frequency = [ x for x,y in Frequency.items() if y == self.frequency ]
|
||||
return self.parent.title + ': ' + frequency[0]
|
||||
|
||||
|
||||
class Meta:
|
||||
|
@ -427,15 +444,17 @@ class Program (Publication):
|
|||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
tag = models.CharField(
|
||||
_('tag')
|
||||
, max_length = 64
|
||||
, help_text = _('used in articles to refer to it')
|
||||
non_stop = models.SmallIntegerField(
|
||||
_('non-stop priority')
|
||||
, help_text = _('this program can be used as non-stop')
|
||||
, default = -1
|
||||
)
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return settings.AIRCOX_PROGRAMS_DATA + slugify(self.title + '_' + self.id)
|
||||
return os.path.join( settings.AIRCOX_PROGRAMS_DIR
|
||||
, slugify(self.title + '_' + str(self.id))
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
@ -455,8 +474,6 @@ class Episode (Publication):
|
|||
parent = models.ForeignKey(
|
||||
Program
|
||||
, verbose_name = _('parent')
|
||||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
tracks = models.ManyToManyField(
|
||||
Track
|
||||
|
@ -473,37 +490,22 @@ class Episode (Publication):
|
|||
|
||||
class Event (Model):
|
||||
"""
|
||||
Event logs and planification of a sound file
|
||||
"""
|
||||
parent = models.ForeignKey (
|
||||
Episode
|
||||
, verbose_name = _('episode')
|
||||
, blank = True
|
||||
, null = True
|
||||
sound = models.ForeignKey (
|
||||
SoundFile
|
||||
, verbose_name = _('sound file')
|
||||
)
|
||||
date = models.DateTimeField( _('date of start') )
|
||||
date_end = models.DateTimeField(
|
||||
_('date of end')
|
||||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
public = models.BooleanField(
|
||||
_('public')
|
||||
, default = False
|
||||
, help_text = _('publication is accessible to the public')
|
||||
type = models.SmallIntegerField(
|
||||
_('type')
|
||||
, choices = [ (y, x) for x,y in EventType.items() ]
|
||||
)
|
||||
date = models.DateTimeField( _('date of event start') )
|
||||
meta = models.TextField (
|
||||
_('meta')
|
||||
, blank = True
|
||||
, null = True
|
||||
)
|
||||
canceled = models.BooleanField( _('canceled'), default = False )
|
||||
|
||||
|
||||
def testify (self):
|
||||
parent = self.parent
|
||||
self.parent.testify()
|
||||
self.parent.date = self.date
|
||||
return self.parent
|
||||
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import os
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
def ensure (key, default):
|
||||
globals()[key] = getattr(settings, key, default)
|
||||
|
||||
|
||||
ensure('AIRCOX_PROGRAMS_DATA', settings.MEDIA_ROOT + '/programs')
|
||||
ensure('AIRCOX_PROGRAMS_DIR',
|
||||
os.path.join(settings.MEDIA_ROOT, 'programs'))
|
||||
ensure('AIRCOX_SOUNDFILE_DEFAULT_DIR',
|
||||
os.path.join(AIRCOX_PROGRAMS_DIR + 'default'))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,76 @@
|
|||
from django.shortcuts import render
|
||||
from django.shortcuts import render
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.utils import timezone, dateformat
|
||||
|
||||
import programs.models as models
|
||||
import programs.settings
|
||||
|
||||
|
||||
|
||||
class EventList:
|
||||
type = None
|
||||
next = None
|
||||
prev = None
|
||||
at = None
|
||||
count = None
|
||||
|
||||
|
||||
def __init__ (self, **kwargs):
|
||||
self.__dict__ = kwargs
|
||||
if kwargs:
|
||||
self.get_queryset()
|
||||
|
||||
|
||||
def get_queryset (self):
|
||||
events = models.Event.objects;
|
||||
|
||||
if self.next: events = events.filter( date_end__ge = timezone.now() )
|
||||
elif self.prev: events = events.filter( date_end__le = timezone.now() )
|
||||
else: events = events.all()
|
||||
|
||||
events = events.extra(order_by = ['date'])
|
||||
if self.at: events = events[self.at:]
|
||||
if self.count: events = events[:self.count]
|
||||
|
||||
self.events = events
|
||||
|
||||
|
||||
def raw_string():
|
||||
"""
|
||||
Return a string with events rendered as raw
|
||||
"""
|
||||
res = []
|
||||
for event in events:
|
||||
r = [ dateformat.format(event.date, "Y/m/d H:i:s")
|
||||
, str(event.type)
|
||||
, event.parent.file.path
|
||||
, event.parent.file.url
|
||||
]
|
||||
|
||||
res.push(' '.join(r))
|
||||
|
||||
return '\n'.join(res)
|
||||
|
||||
|
||||
def json_string():
|
||||
import json
|
||||
|
||||
res = []
|
||||
for event in events:
|
||||
r = {
|
||||
'date': dateformat.format(event.date, "Y/m/d H:i:s")
|
||||
, 'date_end': dateformat.format(event.date_end, "Y/m/d H:i:s")
|
||||
, 'type': str(event.type)
|
||||
, 'file_path': event.parent.file.path
|
||||
, 'file_url': event.parent.file.url
|
||||
}
|
||||
|
||||
res.push(json.dumps(r))
|
||||
|
||||
return '\n'.join(res)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user