Crono

Programming in the future
scheduler \
  .run('hello_future') \
  .every(hours=1)

Getting Started

1. Sign up and install the Crono Python client:

pip install crono

2. Schedule recurring or single occurence jobs in your application (in app.py for example):

import crono

scheduler = crono \
  .Scheduler(token='{token}')

# Timer
scheduler \
  .request('{url}') \
  .in(minutes=1)

# Datetime
scheduler \
  .text('Hello, Future!', 
    from='{phone}', 
    to='{phone}') \
  .on(<datetime>)

# Interval
scheduler \
  .email('Hello, Future!', 
    from='{email}', 
    to='{email}') \
  .every(hours=1)

# Cron
scheduler \
  .run('hello_future', 
    args=[], 
    kwargs={}) \
  .at('0 6 * * 2')

# Decorator
@scheduler.every(days=1)
def hello_future(args, **kwargs):
  print('Hello, Future!')

3. (Optional) Run the Crono server if you want to use the run task in order to call back your application:

crono app:scheduler

Triggers

A trigger defines when a job will be executed. There are 4 types of triggers:

Timer specifies a countdown until the execution of a task. It will only occur once. It is instantiated in the Python client by calling the method in on the Scheduler object. It takes at least 1 keyword argument, hours and/or minutes, of type <int>. Those keywords arguments are cumulative (same for the interval type). For example, hours=1, minutes=30 results in minutes=90.

Python client:
scheduler.in(minutes=30).
Request body:
{
  "trigger": {
    "type": "timer",
    "minutes": 30
  }
}

Datetime specifies the execution of a task at a specific date and time. It will only occur once. It is instantiated in the Python client by calling the method on on the Scheduler object. It takes 1 positional argument of type <datetime.datetime>.

Python client:
import datetime
day = datetime.datetime(2019, 7, 4)
scheduler.on(day).
Request body:
{
  "trigger": {
    "type": "datetime",
    "iso": "2019-07-04T00:00:00"
  }
}

Interval specifies a frequency at which to execute a task. It will occur multiple times. It is instantiated in the Python client by calling the method every on the Scheduler object. It takes at least 1 keyword argument, hours and/or minutes, of type <int>. Those keywords arguments are cumulative (similarly to the timer type). For example, hours=1, minutes=30 results in minutes=90.

Python client:
scheduler.every(hours=1, minutes=30).
Request body:
{
  "trigger": {
    "type": "interval",
    "minutes": 90
  }
}

Cron uses an expression to specify the execution time. It will occur mutiple times. It is instantiated in the Python client by calling the method at on the Scheduler object. It takes exactly 1 positional argument of type <str>.

Python client:
scheduler.at('0 6 * * 2').
Request body:
{
  "trigger": {
    "type": "cron",
    "expression": "0 6 * * 2"
  }
}

Tasks

A task defines what a job will do. There are 4 types of tasks:

request sends an HTTP request. It is powered by the Requests library.

Python client:
scheduler.request(method='GET|POST…', 
  url='{url}', 
  params=<dict|list(tuple)|byte>, 
  data=<dict|list(tuple)|byte|file>, 
  json=<dict>, 
  headers=<dict>, 
  cookies=<dict>, 
  files=<dict>, 
  auth=<tuple>, 
  timeout=<float|tuple>, 
  allow_redirects=<bool>)
  proxy=<dict>, 
  verify=<bool|str>, 
  stream=<bool>, 
  cert=<str|tuple>) \
  .
Request body:
{
  "task": {
    "type": "request",
    "method": "GET|POST…",
    "url": "https://www.crono.com/"
    
  }
}

text sends an SMS. It is powered by Twilio. To use it, you have to specify twilio_account_sid and twilio_auth_id when you instantiate the Scheduler object.

scheduler = crono \
  .Scheduler(token='{token}', 
    twilio_account_sid='{sid}', 
    twilio_auth_id='{id}')

email sends an email. It is powered by Postmark. To use it, you have to specify postmark_api_key and postmark_sender when you instantiate the Scheduler object.

scheduler = crono \
  .Scheduler(token='{token}', 
    postmark_api_key='{key}', 
    postmark_sender='{email}')

run calls a method in your application.

API

Endpoints live under the URL https://api.crono.com/ and generally follow the REST architecture. All requests must be encoded as JSON with the Content-Type: application/json header.

There are 5 endpoints available:

POST /jobs HTTP/1.1
Host: https://api.crono.com/
Content-Type: application/json
Authorization: Bearer {token}

{
  "trigger": {
    "type": "cron",
    "expression": "0 6 * * 2"
  },
  "task": {
    "type": "request",
    "method": "GET",
    "url": "https://www.crono.com/"
  }
}

Advanced

Methods in the Python client are commutative, meaning the trigger can come before or after the task.

scheduler \
  .request('{url}') \
  .in(minutes=1)
# is equivalent to…
scheduler \
  .in(minutes=1) \
  .request('{url}')

Authentication

Requests to the Crono API must provide an API token through the Authorization header. Sign up to request a token.

Python client:
scheduler = crono \
  .Scheduler(token='{token}')
Request header:
Authorization: Bearer {token}

Errors

MissingTrigger

InvalidTriggerType

InvalidTriggerParameters

MissingTask

InvalidTaskType

InvalidTaskParameters

MissingToken

InvalidToken

InsufficientBalance

TODO

Crono is always improving. Here are some of the features coming soon:

Pricing

Crono costs $0.001/execution and your first 100 executions are free (no payment information required).

Support

Email georges.duverger@gmail.com for help.