-
Notifications
You must be signed in to change notification settings - Fork 161
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- on arrête avec watchdog - on prend une simple boucle de polling - un peu de paralellisme
- Loading branch information
Showing
6 changed files
with
104 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
113 changes: 44 additions & 69 deletions
113
zds/tutorialv2/management/commands/publication_watchdog.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,79 +1,54 @@ | ||
from os.path import dirname, join | ||
import os | ||
import logging | ||
import time | ||
|
||
import shutil | ||
from django.core.management import BaseCommand | ||
from pathtools.path import listdir | ||
from watchdog.observers import Observer | ||
from watchdog.events import FileCreatedEvent, FileSystemEventHandler, LoggingEventHandler | ||
from django.conf import settings | ||
from zds.tutorialv2.publication_utils import generate_external_content | ||
from codecs import open | ||
|
||
|
||
class TutorialIsPublished(FileSystemEventHandler): | ||
prepare_callbacks = [] # because we can imagine we will create far more than test directory existence | ||
finish_callbacks = [] # because we can imagine we will send a PM on success or failure one day | ||
|
||
@staticmethod | ||
def __create_dir(extra_contents_path): | ||
if not os.path.exists(extra_contents_path): | ||
os.makedirs(extra_contents_path) | ||
|
||
@staticmethod | ||
def __cleanup_build_and_watchdog(extra_contents_path, watchdog_file_path): | ||
for listed in listdir(extra_contents_path, recursive=False): | ||
try: | ||
shutil.copy(join(extra_contents_path, listed), extra_contents_path.replace('__building', '')) | ||
except Exception: | ||
pass | ||
shutil.rmtree(extra_contents_path) | ||
os.remove(watchdog_file_path) | ||
|
||
def __init__(self): | ||
self.prepare_callbacks = [TutorialIsPublished.__create_dir] | ||
self.finish_callbacks = [TutorialIsPublished.__cleanup_build_and_watchdog] | ||
from pathlib import Path | ||
|
||
def on_created(self, event): | ||
super(TutorialIsPublished, self).on_created(event) | ||
|
||
if isinstance(event, FileCreatedEvent): | ||
with open(event.src_path, encoding='utf-8') as f: | ||
infos = f.read().strip().split(';') | ||
md_file_path = infos[1] | ||
base_name = infos[0] | ||
extra_contents_path = dirname(md_file_path) | ||
self.prepare_generation(extra_contents_path) | ||
try: | ||
generate_external_content(base_name, extra_contents_path, md_file_path, overload_settings=True, | ||
excluded=['watchdog']) | ||
finally: | ||
self.finish_generation(extra_contents_path, event.src_path) | ||
|
||
def prepare_generation(self, extra_contents_path): | ||
|
||
for callback in self.prepare_callbacks: | ||
callback(extra_contents_path) | ||
from django.core.management import BaseCommand | ||
from concurrent.futures import Future, ProcessPoolExecutor | ||
|
||
def finish_generation(self, extra_contents_path, watchdog_file_path): | ||
for callback in self.finish_callbacks: | ||
callback(extra_contents_path, watchdog_file_path) | ||
from zds.tutorialv2.models.database import PublicationEvent, STATE_CHOICES | ||
from zds.tutorialv2.publication_utils import PublicatorRegistry | ||
|
||
|
||
class Command(BaseCommand): | ||
help = 'Launch a watchdog that generate all exported formats (epub, pdf...) files without blocking request handling' | ||
|
||
def handle(self, *args, **options): | ||
path = settings.ZDS_APP['content']['extra_content_watchdog_dir'] | ||
event_handler = TutorialIsPublished() | ||
observer = Observer() | ||
observer.schedule(event_handler, path, recursive=True) | ||
observer.schedule(LoggingEventHandler(), path) | ||
observer.start() | ||
try: | ||
while True: | ||
time.sleep(1) | ||
except KeyboardInterrupt: | ||
observer.stop() | ||
observer.join() | ||
logger = logging.getLogger(Command.__name__) | ||
with ProcessPoolExecutor(5) as executor: | ||
try: | ||
while True: | ||
self.__launch_publicators(executor, logger) | ||
time.sleep(10) | ||
except KeyboardInterrupt: | ||
executor.shutdown(wait=False) | ||
|
||
@staticmethod | ||
def get_callback_of(publication_event: PublicationEvent): | ||
def callback(future: Future): | ||
if future.done(): | ||
publication_event.state_of_processing = 'SUCCESS' | ||
elif future.cancelled(): | ||
publication_event.state_of_processing = 'FAILURE' | ||
publication_event.save() | ||
return callback | ||
|
||
def __launch_publicators(self, executor, logger): | ||
for publication_event in PublicationEvent.objects.filter(state_of_processing=STATE_CHOICES[0][0]): | ||
logger.info('Export %s -- format=%s', publication_event.published_object.title(), | ||
publication_event.format_requested) | ||
content = publication_event.published_object | ||
publicator = PublicatorRegistry.get(publication_event.format_requested) | ||
|
||
extra_content_dir = content.get_extra_contents_directory() | ||
building_extra_content_path = Path(str(Path(extra_content_dir).parent) + '__building', | ||
'extra_contents', content.content_public_slug) | ||
if not building_extra_content_path.exists(): | ||
building_extra_content_path.mkdir(parents=True) | ||
base_name = str(Path(str(building_extra_content_path), content.content_public_slug)) | ||
md_file_path = base_name + '.md' | ||
|
||
future = executor.submit(publicator.publish, md_file_path, base_name) | ||
publication_event.state_of_processing = STATE_CHOICES[1][0] | ||
publication_event.save() | ||
future.add_done_callback(Command.get_callback_of(publication_event)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Generated by Django 2.1.5 on 2019-04-11 12:43 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('tutorialv2', '0023_auto_20190114_1301'), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name='PublicationEvent', | ||
fields=[ | ||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
('state_of_processing', models.CharField(choices=[('REQUESTED', 'Export demandé'), ('RUNNING', 'Export en cours'), ('SUCCESS', 'Export réalisé'), ('FAILURE', 'Export échoué')], max_length=20)), | ||
('format_requested', models.CharField(max_length=25)), | ||
('published_object', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tutorialv2.PublishedContent', verbose_name='contenu publié')), | ||
], | ||
options={ | ||
'verbose_name': 'Événement de publication', | ||
'verbose_name_plural': 'Événements de publication', | ||
}, | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters