| 
					
				 | 
			
			
				@@ -0,0 +1,394 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+Manage talks scheduling in a semantic way 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from __future__ import print_function 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import os 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import io 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from functools import wraps 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import logging 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import re 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import datetime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import shutil 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from copy import copy 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import locale 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from contextlib import contextmanager 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from babel.dates import format_date, format_datetime, format_time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import markdown 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from docutils import nodes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from docutils.parsers.rst import directives, Directive 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+from pelican import signals, generators 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+import jinja2 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+pelican = None  # This will be set during register() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def memoize(function): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '''decorators to cache''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memo = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    @wraps(function) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def wrapper(*args): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if args in memo: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return memo[args] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rv = function(*args) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            memo[args] = rv 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return rv 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return wrapper 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@contextmanager 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def setlocale(name): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    saved = locale.setlocale(locale.LC_ALL) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        yield locale.setlocale(locale.LC_ALL, name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    finally: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        locale.setlocale(locale.LC_ALL, saved) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@memoize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def get_talk_names(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return [name for name in os.listdir(pelican.settings['TALKS_PATH']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if not name.startswith('_') and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            get_talk_data(name) is not None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def all_talks(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return [get_talk_data(tn) for tn in get_talk_names()] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def unique_attr(iterable, attr): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return {x[attr] for x in iterable 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if attr in x} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@memoize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def get_global_data(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fname = os.path.join(pelican.settings['TALKS_PATH'], 'meta.yaml') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not os.path.isfile(fname): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with io.open(fname, encoding='utf8') as buf: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data = yaml.load(buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except Exception: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.exception("Syntax error reading %s; skipping", fname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if data is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if 'startdate' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logging.error("Missing startdate in global data") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data['startdate'] = datetime.datetime.now() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@memoize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def get_talk_data(talkname): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    fname = os.path.join(pelican.settings['TALKS_PATH'], talkname, 'meta.yaml') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if not os.path.isfile(fname): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    with io.open(fname, encoding='utf8') as buf: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data = yaml.load(buf) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.exception("Syntax error reading %s; skipping", fname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if data is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return None 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gridstep = pelican.settings['TALKS_GRID_STEP'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'title' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.warn("Talk <{}> has no `title` field".format(talkname)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['title'] = talkname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'text' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.warn("Talk <{}> has no `text` field".format(talkname)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['text'] = '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'duration' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.info("Talk <{}> has no `duration` field (50min used)" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         .format(talkname)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['duration'] = 50 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data['duration'] = int(data['duration']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if data['duration'] < gridstep: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.info("Talk <{}> lasts only {} minutes; changing to {}" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                         .format(talkname, data['duration'], gridstep)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['duration'] = gridstep 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'links' not in data or not data['links']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['links'] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'contacts' not in data or not data['contacts']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['contacts'] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'needs' not in data or not data['needs']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['needs'] = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'room' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.warn("Talk <{}> has no `room` field".format(talkname)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'time' not in data or 'day' not in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.warn("Talk <{}> has no `time` or `day`".format(talkname)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'time' in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                del data['time'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'day' in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                del data['day'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'day' in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['day'] = get_global_data()['startdate'] + \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    datetime.timedelta(days=data['day']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if 'time' in data and 'day' in data: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            timeparts = re.findall(r'\d+', str(data['time'])) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 4 > len(timeparts) > 0: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                timeparts = [int(p) for p in timeparts] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                data['time'] = datetime.datetime.combine( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    data['day'], datetime.time(*timeparts)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                logging.error("Talk <%s> has malformed `time`", talkname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data['id'] = talkname 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        resdir = os.path.join(pelican.settings['TALKS_PATH'], talkname, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              pelican.settings['TALKS_ATTACHMENT_PATH']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if os.path.isdir(resdir) and os.listdir(resdir): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            data['resources'] = resdir 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return data 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    except: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        logging.exception("Error on talk %s", talkname) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        raise 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+@memoize 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def jinja_env(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    env = jinja2.Environment( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        loader=jinja2.FileSystemLoader(os.path.join(pelican.settings['TALKS_PATH'], '_templates')), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        autoescape=True, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    env.filters['markdown'] = lambda text: \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            jinja2.Markup(markdown.Markdown(extensions=['meta']). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                          convert(text)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    env.filters['dateformat'] = format_date 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    env.filters['datetimeformat'] = format_datetime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    env.filters['timeformat'] = format_time 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return env 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TalkListDirective(Directive): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    final_argument_whitespace = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    has_content = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    option_spec = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        'lang': directives.unchanged 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def run(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        lang = self.options.get('lang', 'C') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tmpl = jinja_env().get_template('talk.html') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        def _sort_date(name): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            This function is a helper to sort talks by start date 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            When no date is available, put at the beginning 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            ''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            d = get_talk_data(name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'time' in d: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return d['time'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return datetime.datetime(1, 1, 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nodes.raw('', tmpl.render(lang=lang, **get_talk_data(n)), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      format='html') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for n in sorted(get_talk_names(), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            key=_sort_date) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                                ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TalkDirective(Directive): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    required_arguments = 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    final_argument_whitespace = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    has_content = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    option_spec = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        'lang': directives.unchanged 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def run(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        lang = self.options.get('lang', 'C') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tmpl = jinja_env().get_template('talk.html') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        data = get_talk_data(self.arguments[0]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if data is None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            nodes.raw('', tmpl.render(lang=lang, **data), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      format='html') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TalkGridDirective(Directive): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    '''A complete grid''' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    final_argument_whitespace = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    has_content = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    option_spec = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        'lang': directives.unchanged 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def run(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        lang = self.options.get('lang', 'C') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        tmpl = jinja_env().get_template('grid.html') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        output = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        days = unique_attr(all_talks(), 'day') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        gridstep = pelican.settings['TALKS_GRID_STEP'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for day in sorted(days): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            talks = {talk['id'] for talk in all_talks() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     if talk.get('day', None) == day 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     and 'time' in talk 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     and 'room' in talk} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if not talks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            talks = [get_talk_data(t) for t in talks] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rooms = set() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for t in talks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if type(t['room']) is list: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for r in t['room']: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        rooms.add(r) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    rooms.add(t['room']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            rooms = list(sorted(rooms)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            # room=* is not a real room. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            # Remove it unless that day only has special rooms 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if '*' in rooms and len(rooms) > 1: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                del rooms[rooms.index('*')] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            mintime = min({talk['time'].hour * 60 + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           talk['time'].minute 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           for talk in talks}) // gridstep * gridstep 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            maxtime = max({talk['time'].hour * 60 + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           talk['time'].minute + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           talk['duration'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           for talk in talks}) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            times = {} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for t in range(mintime, maxtime, gridstep): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                times[t] = [None] * len(rooms) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for talk in sorted(talks, key=lambda x: x['time']): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                talktime = talk['time'].hour * 60 + talk['time'].minute 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                position = talktime // gridstep * gridstep  # round 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                assert position in times 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if talk['room'] == '*': 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    roomnums = range(len(rooms)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                elif type(talk['room']) is list: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    roomnums = [rooms.index(r) for r in talk['room']] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    roomnums = [rooms.index(talk['room'])] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for roomnum in roomnums: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if times[position][roomnum] is not None: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        logging.error("Talk %s and %s overlap! (room %s)", 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      times[position][roomnum]['id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      talk['id'], 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      rooms[roomnum] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        continue 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    times[position][roomnum] = copy(talk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    times[position][roomnum]['skip'] = False 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for i in range(1, talk['duration'] // gridstep): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        times[position + i*gridstep][roomnum] = copy(talk) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        times[position + i*gridstep][roomnum]['skip'] = True 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            render = tmpl.render(times=times, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 rooms=rooms, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 mintime=mintime, maxtime=maxtime, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 timestep=gridstep, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 lang=lang, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                 ) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            output.append(nodes.raw( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                '', u'<h4>%s</h4>' % format_date(day, format='full', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                 locale=lang), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                format='html')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            output.append(nodes.raw('', render, format='html')) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return output 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def talks_to_ics(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content = u'BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:pelican\n' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    for t in all_talks(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            content += talk_to_ics(t) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        except: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            logging.exception("Error producing calendar for talk %s", t['id']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += 'END:VCALENDAR\n' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return content 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def talk_to_ics(talk): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if 'time' not in talk or 'duration' not in talk: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return '' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    start = talk['time'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    end = start + datetime.timedelta(minutes=talk['duration']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content = 'BEGIN:VEVENT\n' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "UID:%s@%d.hackmeeting.org\n" % (talk['id'], talk['day'].year) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "SUMMARY:%s\n" % talk['title'] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "DTSTAMP:%s\n" % time.strftime('%Y%m%dT%H%M%SZ', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                              time.gmtime(float( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                  start.strftime('%s')))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "DTSTART:%s\n" % time.strftime('%Y%m%dT%H%M%SZ', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                              time.gmtime(float( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                  start.strftime('%s')))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "DTEND:%s\n" % time.strftime('%Y%m%dT%H%M%SZ', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                            time.gmtime(float( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                                  end.strftime('%s')))) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += "LOCATION:%s\n" % (talk['room'] if 'room' in talk else 'todo') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    content += 'END:VEVENT\n' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return content 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+class TalksGenerator(generators.Generator): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def __init__(self, *args, **kwargs): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.talks = [] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        super(TalksGenerator, self).__init__(*args, **kwargs) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def generate_context(self): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self.talks = {n: get_talk_data(n) for n in get_talk_names()} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        self._update_context(('talks',)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def generate_output(self, writer=None): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        for talkname in self.talks: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if 'resources' in self.talks[talkname]: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                outdir = os.path.join(self.output_path, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      pelican.settings['TALKS_PATH'], talkname, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      pelican.settings['TALKS_ATTACHMENT_PATH']) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if os.path.isdir(outdir): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    shutil.rmtree(outdir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                shutil.copytree(self.talks[talkname]['resources'], outdir) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        with io.open(os.path.join(self.output_path, pelican.settings.get('TALKS_ICS')), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     'w', 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     encoding='utf8') as buf: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            buf.write(talks_to_ics()) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def add_talks_option_defaults(pelican): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pelican.settings.setdefault('TALKS_PATH', 'talks') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pelican.settings.setdefault('TALKS_ATTACHMENT_PATH', 'res') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pelican.settings.setdefault('TALKS_ICS', 'schedule.ics') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pelican.settings.setdefault('TALKS_GRID_STEP', 30) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def get_generators(gen): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return TalksGenerator 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+def pelican_init(pelicanobj): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    global pelican 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    pelican = pelicanobj 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+try: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    import yaml 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+except ImportError: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    print('ERROR: yaml not found. Talks plugins will be disabled') 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def register(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        pass 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+else: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    def register(): 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        signals.initialized.connect(pelican_init) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        signals.get_generators.connect(get_generators) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        signals.initialized.connect(add_talks_option_defaults) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        directives.register_directive('talklist', TalkListDirective) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        directives.register_directive('talk', TalkDirective) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        directives.register_directive('talkgrid', TalkGridDirective) 
			 |