Переглянути джерело

Removes clickcounter tutorial

Ask Solem 13 роки тому
батько
коміт
6a4f6e10a8

+ 11 - 12
docs/faq.rst

@@ -657,28 +657,27 @@ How can I run a task once another task has finished?
 ----------------------------------------------------
 
 **Answer**: You can safely launch a task inside a task.
-Also, a common pattern is to use callback tasks:
+Also, a common pattern is to add callbacks to tasks:
 
 .. code-block:: python
 
-    @celery.task()
-    def add(x, y, callback=None):
-        result = x + y
-        if callback:
-            subtask(callback).delay(result)
-        return result
+    from celery.utils.log import get_task_logger
+
+    logger = get_task_logger(__name__)
 
+    @celery.task()
+    def add(x, y):
+        return x + y
 
     @celery.task(ignore_result=True)
-    def log_result(result, **kwargs):
-        logger = log_result.get_logger(**kwargs)
-        logger.info("log_result got: %s" % (result, ))
+    def log_result(result):
+        logger.info("log_result got: %r" % (result, ))
 
 Invocation::
 
-    >>> add.delay(2, 2, callback=log_result.subtask())
+    >>> (add.s(2, 2) | log_result.s()).delay()
 
-See :doc:`userguide/tasksets` for more information.
+See :doc:`userguide/canvas` for more information.
 
 .. _faq-cancel-task:
 

+ 3 - 3
docs/getting-started/next-steps.rst

@@ -141,10 +141,10 @@ These options are described in more detailed in the :ref:`Workers Guide <guide-w
         $ celery --app=proj.celery:celery
 
 
-.. _designing-work-flows:
+.. _designing-workflows:
 
-Designing Work-flows
-====================
+*Canvas*: Designing Workflows
+=============================
 
 A :func:`~celery.subtask` wraps the signature of a single task invocation:
 arguments, keyword arguments and execution options.

+ 1 - 1
docs/internals/guide.rst

@@ -64,7 +64,7 @@ Naming
         Sometimes it makes sense to have a class mask as a function,
         and there is precedence for this in the stdlib (e.g.
         :class:`~contextlib.contextmanager`).  Celery examples include
-        :class:`~celery.task.sets.subtask`, :class:`~celery.task.chords.chord`,
+        :class:`~celery.subtask`, :class:`~celery.chord`,
         ``inspect``, :class:`~kombu.utils.functional.promise` and more..
 
 - Factory functions and methods must be `CamelCase` (excluding verbs):

+ 1 - 1
docs/reference/celery.task.base.rst

@@ -1,5 +1,5 @@
 ===================================
- celery.task.base
+ celery.task.base (Deprecated)
 ===================================
 
 .. contents::

+ 0 - 11
docs/reference/celery.task.sets.rst

@@ -1,11 +0,0 @@
-======================================================
- celery.task.sets
-======================================================
-
-.. contents::
-    :local:
-.. currentmodule:: celery.task.sets
-
-.. automodule:: celery.task.sets
-    :members:
-    :undoc-members:

+ 0 - 1
docs/reference/index.rst

@@ -20,7 +20,6 @@
     celery.app.utils
     celery.task
     celery.task.base
-    celery.task.sets
     celery.result
     celery.task.http
     celery.schedules

+ 0 - 242
docs/tutorials/clickcounter.rst

@@ -1,242 +0,0 @@
-.. _tut-clickcounter:
-
-============================================================
- Tutorial: Creating a click counter using Kombu and celery
-============================================================
-
-.. contents::
-    :local:
-
-Introduction
-============
-
-A click counter should be easy, right? Just a simple view that increments
-a click in the DB and forwards you to the real destination.
-
-This would work well for most sites, but when traffic starts to increase,
-you are likely to bump into problems. One database write for every click is
-not good if you have millions of clicks a day.
-
-So what can you do? In this tutorial we will send the individual clicks as
-messages using `kombu`, and then process them later with a Celery periodic
-task.
-
-Celery and Kombu are excellent in tandem, and while this might not be
-the perfect example, you'll at least see one example how of they can be used
-to solve a task.
-
-The model
-=========
-
-The model is simple, `Click` has the URL as primary key and a number of
-clicks for that URL. Its manager, `ClickManager` implements the
-`increment_clicks` method, which takes a URL and by how much to increment
-its count by.
-
-
-*clickmuncher/models.py*:
-
-.. code-block:: python
-
-    from django.db import models
-    from django.utils.translation import ugettext_lazy as _
-
-
-    class ClickManager(models.Manager):
-
-        def increment_clicks(self, for_url, increment_by=1):
-            """Increment the click count for an URL.
-
-                >>> Click.objects.increment_clicks("http://google.com", 10)
-
-            """
-            click, created = self.get_or_create(url=for_url,
-                                    defaults={"click_count": increment_by})
-            if not created:
-                click.click_count += increment_by
-                click.save()
-
-            return click.click_count
-
-
-    class Click(models.Model):
-        url = models.URLField(_(u"URL"), verify_exists=False, unique=True)
-        click_count = models.PositiveIntegerField(_(u"click_count"),
-                                                  default=0)
-
-        objects = ClickManager()
-
-        class Meta:
-            verbose_name = _(u"URL clicks")
-            verbose_name_plural = _(u"URL clicks")
-
-Using Kombu to send clicks as messages
-========================================
-
-The model is normal django stuff, nothing new there. But now we get on to
-the messaging. It has been a tradition for me to put the projects messaging
-related code in its own `messaging.py` module, and I will continue to do so
-here so maybe you can adopt this practice. In this module we have two
-functions:
-
-* `send_increment_clicks`
-
-  This function sends a simple message to the broker. The message body only
-  contains the URL we want to increment as plain-text, so the exchange and
-  routing key play a role here. We use an exchange called `clicks`, with a
-  routing key of `increment_click`, so any consumer binding a queue to
-  this exchange using this routing key will receive these messages.
-
-* `process_clicks`
-
-  This function processes all currently gathered clicks sent using
-  `send_increment_clicks`. Instead of issuing one database query for every
-  click it processes all of the messages first, calculates the new click count
-  and issues one update per URL. A message that has been received will not be
-  deleted from the broker until it has been acknowledged by the receiver, so
-  if the receiver dies in the middle of processing the message, it will be
-  re-sent at a later point in time. This guarantees delivery and we respect
-  this feature here by not acknowledging the message until the clicks has
-  actually been written to disk.
-
-  .. note::
-
-    This could probably be optimized further with
-    some hand-written SQL, but it will do for now. Let's say it's an exercise
-    left for the picky reader, albeit a discouraged one if you can survive
-    without doing it.
-
-On to the code...
-
-*clickmuncher/messaging.py*:
-
-.. code-block:: python
-
-    from celery.messaging import establish_connection
-    from kombu.compat import Publisher, Consumer
-    from clickmuncher.models import Click
-
-
-    def send_increment_clicks(for_url):
-        """Send a message for incrementing the click count for an URL."""
-        connection = establish_connection()
-        publisher = Publisher(connection=connection,
-                              exchange="clicks",
-                              routing_key="increment_click",
-                              exchange_type="direct")
-
-        publisher.send(for_url)
-
-        publisher.close()
-        connection.close()
-
-
-    def process_clicks():
-        """Process all currently gathered clicks by saving them to the
-        database."""
-        connection = establish_connection()
-        consumer = Consumer(connection=connection,
-                            queue="clicks",
-                            exchange="clicks",
-                            routing_key="increment_click",
-                            exchange_type="direct")
-
-        # First process the messages: save the number of clicks
-        # for every URL.
-        clicks_for_url = {}
-        messages_for_url = {}
-        for message in consumer.iterqueue():
-            url = message.body
-            clicks_for_url[url] = clicks_for_url.get(url, 0) + 1
-            # We also need to keep the message objects so we can ack the
-            # messages as processed when we are finished with them.
-            if url in messages_for_url:
-                messages_for_url[url].append(message)
-            else:
-                messages_for_url[url] = [message]
-
-        # Then increment the clicks in the database so we only need
-        # one UPDATE/INSERT for each URL.
-        for url, click_count in clicks_for_urls.items():
-            Click.objects.increment_clicks(url, click_count)
-            # Now that the clicks has been registered for this URL we can
-            # acknowledge the messages
-            [message.ack() for message in messages_for_url[url]]
-
-        consumer.close()
-        connection.close()
-
-
-View and URLs
-=============
-
-This is also simple stuff, don't think I have to explain this code to you.
-The interface is as follows, if you have a link to http://google.com you
-would want to count the clicks for, you replace the URL with:
-
-    http://mysite/clickmuncher/count/?u=http://google.com
-
-and the `count` view will send off an increment message and forward you to
-that site.
-
-*clickmuncher/views.py*:
-
-.. code-block:: python
-
-    from django.http import HttpResponseRedirect
-    from clickmuncher.messaging import send_increment_clicks
-
-
-    def count(request):
-        url = request.GET["u"]
-        send_increment_clicks(url)
-        return HttpResponseRedirect(url)
-
-
-*clickmuncher/urls.py*:
-
-.. code-block:: python
-
-    from django.conf.urls.defaults import patterns, url
-    from clickmuncher import views
-
-    urlpatterns = patterns("",
-        url(r'^$', views.count, name="clickmuncher-count"),
-    )
-
-
-Creating the periodic task
-==========================
-
-Processing the clicks every 30 minutes is easy using celery periodic tasks.
-
-*clickmuncher/tasks.py*:
-
-.. code-block:: python
-
-    from celery.task import PeriodicTask
-    from clickmuncher.messaging import process_clicks
-    from datetime import timedelta
-
-
-    class ProcessClicksTask(PeriodicTask):
-        run_every = timedelta(minutes=30)
-
-        def run(self, **kwargs):
-            process_clicks()
-
-We subclass from :class:`celery.task.base.PeriodicTask`, set the `run_every`
-attribute and in the body of the task just call the `process_clicks`
-function we wrote earlier.
-
-
-Finishing
-=========
-
-There are still ways to improve this application. The URLs could be cleaned
-so the URL http://google.com and http://google.com/ is the same. Maybe it's
-even possible to update the click count using a single UPDATE query?
-
-If you have any questions regarding this tutorial, please send a mail to the
-mailing-list or come join us in the #celery IRC channel at Freenode:
-http://celeryq.org/introduction.html#getting-help

+ 0 - 1
docs/tutorials/index.rst

@@ -10,5 +10,4 @@
 
     daemonizing
     debugging
-    clickcounter
     task-cookbook

+ 15 - 19
docs/userguide/groups.rst → docs/userguide/canvas.rst

@@ -1,22 +1,20 @@
-.. _guide-sets:
-.. _guide-groups:
+.. _guide-canvas:
 
-=======================================
- Groups, Chords, Chains and Callbacks
-=======================================
+============================
+ Canvas: Building Workflows
+============================
 
 .. contents::
     :local:
 
-.. _sets-subtasks:
-.. _groups-subtasks:
+.. _canvas-subtasks:
 
 Subtasks
 ========
 
 .. versionadded:: 2.0
 
-The :class:`~celery.task.sets.subtask` type is used to wrap the arguments and
+The :class:`~celery.subtask` type is used to wrap the arguments and
 execution options for a single task invocation:
 
 .. code-block:: python
@@ -31,7 +29,7 @@ For convenience every task also has a shortcut to create subtasks:
 
     task.subtask(args, kwargs, options)
 
-:class:`~celery.task.sets.subtask` is actually a :class:`dict` subclass,
+:class:`~celery.subtask` is actually a :class:`dict` subclass,
 which means it can be serialized with JSON or other encodings that doesn't
 support complex Python objects.
 
@@ -43,8 +41,7 @@ Also it can be regarded as a type, as the following usage works::
 
 This makes it excellent as a means to pass callbacks around to tasks.
 
-.. _sets-callbacks:
-.. _groups-callbacks:
+.. _canvas-callbacks:
 
 Callbacks
 ---------
@@ -79,17 +76,16 @@ arguments::
 As expected this will first launch one task calculating :math:`2 + 2`, then
 another task calculating :math:`4 + 8`.
 
-.. _sets-taskset:
-.. _groups-group:
+.. _canvas-group:
 
 Groups
 ======
 
-The :class:`~celery.task.sets.group` enables easy invocation of several
+The :class:`~celery.group` enables easy invocation of several
 tasks at once, and is then able to join the results in the same order as the
 tasks were invoked.
 
-``group`` takes a list of :class:`~celery.task.sets.subtask`'s::
+``group`` takes a list of :class:`~celery.subtask`'s::
 
     >>> from celery import group
     >>> from tasks import add
@@ -115,12 +111,12 @@ The first argument can alternatively be an iterator, like::
 
     >>> group(add.subtask((i, i)) for i in range(100))
 
-.. _sets-results:
+.. _canvas-group-results:
 
-Results
--------
+Group Results
+-------------
 
-When a  :class:`~celery.task.sets.group` is applied it returns a
+When a  :class:`~celery.group` is applied it returns a
 :class:`~celery.result.TaskSetResult` object.
 
 :class:`~celery.result.TaskSetResult` takes a list of

+ 1 - 1
docs/userguide/index.rst

@@ -15,7 +15,7 @@
     calling
     workers
     periodic-tasks
-    groups
+    canvas
     remote-tasks
     routing
     monitoring

+ 1 - 1
docs/userguide/overview.rst

@@ -32,7 +32,7 @@ a "task".
 
 * Go to :ref:`guide-tasks`.
 * Go to :ref:`guide-calling`.
-* Go to :ref:`guide-sets`
+* Go to :ref:`guide-canvas`
 * Go to :ref:`guide-beat`.
 * Go to :ref:`guide-webhooks`.
 

+ 1 - 1
docs/userguide/tasks.rst

@@ -990,7 +990,7 @@ Make your design asynchronous instead, for example by using *callbacks*.
 Here we instead create a chain of tasks by linking together
 different :func:`~celery.subtask`'s.
 You can read about chains and other powerful constructs
-at :ref:`designing-work-flows`.
+at :ref:`designing-workflows`.
 
 .. _task-performance-and-strategies:
 

+ 0 - 1
docs/userguide/tasksets.rst

@@ -1 +0,0 @@
-groups.rst