Modules

cpucoolerchart

CPU cooler performance and price database.

cpucoolerchart.__version__ = '0.1rc1'

The version number (follows PEP 440)

cpucoolerchart.app

This module creates the WSGI application object.

cpucoolerchart.app.DEFAULT_CONFIG = {'SQLALCHEMY_DATABASE_URI': 'sqlite:///{INSTANCE_PATH}/development.db', 'CACHE_KEY_PREFIX': 'cpucoolerchart:', 'HEROKU_WORKER_NAME': 'worker', 'CACHE_TYPE': 'filesystem', 'HEROKU_API_KEY': None, 'USE_QUEUE': False, 'START_WORKER_NODE': None, 'DANAWA_API_KEY_SEARCH': None, 'DANAWA_API_KEY_PRODUCT_INFO': None, 'CACHE_DIR': '{INSTANCE_PATH}/cache', 'UPDATE_INTERVAL': 86400, 'RQ_URL': None, 'CACHE_DEFAULT_TIMEOUT': 10800, 'HEROKU_APP_NAME': None, 'ACCESS_CONTROL_ALLOW_ORIGIN': '*'}

Default configuration values for the app. Note that {INSTANCE_PATH} will be converted to the absolute path of instance directory under the current working directiry.

cpucoolerchart.app.create_app(config=None)[source]

Returns a CPU Cooler Chart Flask app. Configuration is applied in the following order:

  • DEFAULT_CONFIG
  • python file that the CPUCOOLERCHART_SETTINGS environment variable points to, if exists
  • config argument, if provided. If it is a string, the file it points to (relative to the current working directory or absolute) is read, otherwise it is assumed to be a mapping.

For more information, see Configuration Handling.

cpucoolerchart.cache

Implements custom caches.

class cpucoolerchart.cache.CompressedRedisCache(*args, **kwargs)[source]

werkzeug.contrib.cache.RedisCache with data compression. Values are transparently compressed and decompressed when storing and fetching.

To use this cache, set CACHE_TYPE to "cpucoolerchart.cache.compressedredis" when configuring the app.

cpucoolerchart.cache.compressedredis(app, config, args, kwargs)[source]

Returns a CompressedRedisCache. Compatible with Flask-Cache.

cpucoolerchart.command

Various commands to manage the database. You can run the commands by typing cpucoolerchart [command] if you’ve installed the package, or python manage.py [command] if you’ve downloaded the source.

cpucoolerchart.command.main(app=None)[source]

Runs the command manager that parses the command line arguments and executes the given command. If app is not given, it will create a new app using create_app().

The following functions are commands that can be run from command line. Normally you do not call these functions directly in Python modules, but if you want here is an example:

from cpucoolerchart.app import create_app
from cpucoolerchart.command import update
app = create_app()  # or any app that you've created before
with app.app_context():
    update(force=True)
cpucoolerchart.command.update(force=False)[source]

Updates the database with data fetched from remote sources (Coolenjoy and Danawa). If –force is used, always update the database even if it is done recently. Note that even if –force is used, the updated data might not be up-to-date since responses from remote sources are cached. If you really want to make sure the data is fresh, run clearcache before this command.

cpucoolerchart.command.createdb()[source]

Creates tables in the database. It first checks for the existence of each individual table, and if not found will issue the CREATE statements.

cpucoolerchart.command.dropdb()[source]

Drops all database tables.

cpucoolerchart.command.resetdb()[source]

Drops and creates all database tables. It’s just a shortcut for dropdb and createdb in succession.

cpucoolerchart.command.clearcache()[source]

Clears the cache which may contain returned HTML pages from Coolenjoy, the time when the database was updated, etc.

cpucoolerchart.command.export(delim=', ')[source]

Prints all data in a comma-separated format. Use –delim to change the delimeter to other than a comma.

cpucoolerchart.command.danawa()[source]

Searches Danawa for heatsinks that don’t have entries in DANAWA_ID_MAPPING and prints results. It is useful during development to find missing Danawa identifiers for heatsinks.

cpucoolerchart.crawler

Implements functions for fetching and organizing data from Coolenjoy and Danawa.

cpucoolerchart.crawler.NOISE_MAX = 100

Constant for maximum noise level. It does not represent an actual value.

cpucoolerchart.crawler.NOISE_LEVELS = [35, 40, 45, 100]

List of noise levels in dB for which the measurements are taken

cpucoolerchart.crawler.CPU_POWER = [62, 92, 150, 200]

List of CPU power consumptions in watt for which the measurements are taken

cpucoolerchart.crawler.ORDER_BY = ('maker', 'model', 'fan_size', 'fan_thickness', 'fan_count', 'noise', 'power', 'noise_actual_min')

Default sorting order for measurement data

cpucoolerchart.crawler.DEPENDENCIES = {('maker', 'model', 'fan_size', 'fan_thickness', 'fan_count', 'noise', 'power'): ('noise_actual_min', 'noise_actual_max', 'rpm_min', 'rpm_max', 'cpu_temp_delta', 'power_temp_delta'), ('maker', 'model'): ('width', 'depth', 'height', 'heatsink_type', 'weight')}

Theoretical depedencies between properties to check integrity of the original data. There should be, if any, very small number of violations of these deps. Request corrections to Coolenjoy if you find the data too inconsistent.

cpucoolerchart.crawler.is_update_needed()[source]

Returns True if the data is not updated recently, which means that it is not updated for more than UPDATE_INTERVAL seconds since the last update.

cpucoolerchart.crawler.update_data(force=False)[source]

Updates the database with data fetched from remote sources (Coolenjoy and Danawa). If force is True, always update the database even if it is done recently. Note that even if force is used, the updated data might not be up-to-date since responses from remote sources are cached.

cpucoolerchart.crawler.print_danawa_results()[source]

Searches Danawa for heatsinks that don’t have entries in DANAWA_ID_MAPPING and prints results. It is useful to find missing Danawa identifiers for heatsinks.

cpucoolerchart.crawler_data

Defines rules to compensate for inconsistencies in Coolenjoy data and Danawa product identifiers.

cpucoolerchart.crawler_data.MAKER_FIX = {...}

Mapping that encodes rules to rewrite inconsistent maker names.

cpucoolerchart.crawler_data.MODEL_FIX = {...}

Mapping that encodes rules to rewrite inconsistent heatsink model names.

cpucoolerchart.crawler_data.INCONSISTENCY_FIX = {...}

Mapping that encodes rules to fix inconsistent model data such as width and depth.

cpucoolerchart.crawler_data.DANAWA_ID_MAPPING = {...}

Mapping that encodes rules to map each heatsink model to corresponding Danawa product identifier.

cpucoolerchart.extensions

Contains Flask extension objects used by the CPU Cooler Chart app.

class cpucoolerchart.extensions.Redis(app=None)[source]

Extends redis.Redis so that it works with Flask app. If app is specified, a connection is initialized with init_app().

init_app(app)[source]

Initializes a Redis connection. The following configuration values are read from app.config:

name description
RQ_HOST Redis server host.
RQ_PORT Redis server port. Default is 6379.
RQ_DB Redis db (zero-based number index). Default is 0.
RQ_PASSWORD Redis server password.
RQ_URL Redis URL that can specify host, port, db, password in a single value. Example: redis://user:password@host:6379/0. If defined, it overrides all the other values above.
cpucoolerchart.extensions.cache = <flask_cache.Cache object at 0x3effa90>

Flask-Cache instance

cpucoolerchart.extensions.db = <SQLAlchemy engine=None>

Flask-SQLAlchemy instance

cpucoolerchart.extensions.redis

Redis instance

cpucoolerchart.extensions.update_queue = Queue('update')

A Redis queue that is used to update data

cpucoolerchart.models

Defines ORM models to store data persistently.

class cpucoolerchart.models.BaseModel(**kwargs)[source]

Extends db.Model. All models inherit this base.

as_dict()[source]

Returns columns as a mapping. Example:

>>> class Person(BaseModel):
...    name = db.Column(db.String(100), primary_key=True)
...    age = db.Column(db.Integer)
...
>>> Person(name='bob', age=24).as_dict()
{'name': 'bob', 'age': 24}
update(**kwargs)[source]

Updates the current instance. Example:

>>> class Person(BaseModel):
...    name = db.Column(db.String(100), primary_key=True)
...
>>> person = Person(name='bob')
>>> person.update(name='john')
>>> person.name
'john'

If all specified values are the same with the current values, no assignment occurs so that db.session.dirty is not changed.

class cpucoolerchart.models.FanConfig(**kwargs)[source]

Represents a combination of a heatsink and one or more fans.

class cpucoolerchart.models.Heatsink(**kwargs)[source]

Represents a heatsink.

class cpucoolerchart.models.Maker(**kwargs)[source]

Represents a company that makes heatsinks.

class cpucoolerchart.models.Measurement(**kwargs)[source]

Represents a measurement for a specific fan config under a specific fan noise and CPU power consumption target.

cpucoolerchart.views

Defines view functions and helpers.

cpucoolerchart.views.all(*args, **kwargs)[source]

Returns all data in CSV format.

Example request:

GET /all HTTP/1.1
Host: example.com
Accept: */*

Example response:

HTTP/1.1 200 OK
Content-Disposition: filename="cooler.csv"
Content-Type: text/csv; charset=utf-8

maker,model,width,depth,height,heatsink_type,weight,price,shop_count,first_seen,fan_size,fan_thickness,fan_count,noise,noise_actual_min,noise_actual_max,rpm_min,rpm_max,power,cpu_temp_delta,power_temp_delta
3Rsystem,iCEAGE 120,125.0,100.0,154.0,tower,590.0,,,2007-04-04 16:18:55,120,25,1,35,,,1002,1010,62,50.7,
Zalman,ZM-LQ320,,,,tower,195.0,91000,244,2013-01-31 16:57:18,120,25,2,100,58,58,2042,2068,200,60.8,64.5
cpucoolerchart.views.crossdomain(origin=None, methods=None, headers=None, max_age=2592000, attach_to_all=True, automatic_options=True)[source]

Decorater that makes a view function compatible with cross-site HTTP requests by attaching Access-Control-* response headers. See HTTP access control (CORS) for more information.

Parameters:
  • origin (str or collections.Iterable) – allowed URIs (Access-Control-Allow-Origin response header). If it is None, the value of ACCESS_CONTROL_ALLOW_ORIGIN in the current app’s config is used.
  • methods (str or collections.Iterable) – allowed HTTP methods (Access-Control-Allow-Methods response header)
  • headers (str or collections.Iterable) – allowed HTTP headers (Access-Control-Allow-Headers response header)
  • max_age (int or datetime.timedelta) – how long the results of a preflight request can be cached (Access-Control-Max-Age response header)
  • attach_to_all (bool) – If False, the response is unmodified unless the request method is OPTIONS.
  • automatic_options (bool) – If True, make a default response for OPTIONS requests using Flask.make_default_options_response().
cpucoolerchart.views.export_data(delim=', ')[source]

Returns all data in CSV format.

cpucoolerchart.views.fan_configs(*args, **kwargs)[source]

Returns all fan configs, combinations of a heatsink and one or more fans. CORS enabled.

Example request:

GET /fan-configs HTTP/1.1
Host: example.com
Accept: application/json

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "count": 2,
  "items": [
    {
      "fan_count": 1,
      "fan_size": 120,
      "fan_thickness": 25,
      "heatsink_id": 1,
      "id": 1
    },
    {
      "fan_count": 1,
      "fan_size": 120,
      "fan_thickness": 25,
      "heatsink_id": 2,
      "id": 2
    }
  ]
}

Properties:

name type description
id number Internal identifier for a fan config
heatsink_id number id of the corresponding heatsink
fan_count number Number of fans. Note that all fans in a single fan have the same fan size and thickness.
fan_size number The diameter of a fan in mm
fan_thickness number The thickness of a fan in mm
cpucoolerchart.views.heatsinks(*args, **kwargs)[source]

Returns all heatsink models. CORS enabled.

Example request:

GET /heatsinks HTTP/1.1
Host: example.com
Accept: application/json

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "count": 1,
  "items": [
    {
      "danawa_id": 1465177,
      "depth": null,
      "first_seen": "Fri, 12 Aug 2011 08:03:08 GMT",
      "heatsink_type": "tower",
      "height": null,
      "id": 46,
      "image_url": "http://img.d.com/prod_img/177/465/img/145177_1.jpg",
      "maker_id": 12,
      "name": "H100",
      "price": 184760,
      "shop_count": 5,
      "weight": null,
      "width": null
    }
  ]
}

Properties:

name type description
id number Internal identifier for a heatsink
maker_id number id of the corresponding maker
name string Name of the heatsink
heatsink_type string Type of the heatsink. Currently there are two types: flower and tower.
width* number Width of the heatsink in mm
depth* number Depth of the heatsink in mm
height* number Height of the heatsink in mm
weight* number Weight of the heatsink in g
danawa_id* number Danawa identifier for the heatsink
price* number Lowest price of the heatsink on Danawa in KRW
shop_count* number Number of stores selling the heatsink on Danawa
first_seen* date Time when the heatsink first appeared on Danawa
image_url* string URL of the photo of the heatsink
cpucoolerchart.views.makers(*args, **kwargs)[source]

Returns all heatsink makers. CORS enabled.

Example request:

GET /makers HTTP/1.1
Host: example.com
Accept: application/json

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "count": 2,
  "items": [
    {
      "id": 1,
      "name": "CoolerMaster"
    },
    {
      "id": 2,
      "name": "Corsair"
    }
  ]
}

Properties:

name type description
id number Internal identifier for a maker
name string Name of the maker
cpucoolerchart.views.measurements(*args, **kwargs)[source]

Returns all measurement data. CORS enabled.

Example request:

GET /measurements HTTP/1.1
Host: example.com
Accept: application/json

Example response:

HTTP/1.1 200 OK
Content-Type: application/json

{
  "count": 1,
  "items": [
    {
      "cpu_temp_delta": 50.7,
      "fan_config_id": 1,
      "id": 1,
      "noise": 35,
      "noise_actual_max": null,
      "noise_actual_min": null,
      "power": 62,
      "power_temp_delta": null,
      "rpm_max": 1010,
      "rpm_min": 1002
    }
  ]
}

Properties:

name type description
id number Internal identifier for a measurement
fan_config_id number id of the corresponding fan config
noise number Target noise level in dB. All values are literal but a value of 100 means the maximum noise level that the fan config can get. In this case you may look noise_actual_min and noise_actual_max for the range of actual values, although these values can be missing even noise is 100.
noise_actual_min* number The minimum measured value of the actual noise level in dB
noise_actual_max* number The maximum measured value of the actual noise level in dB
power number Target CPU power consumption in watt.
rpm_min* number The minimum measured value of RPM of fans
rpm_max* number The maximum measured value of RPM of fans
cpu_temp_delta number CPU temperature in °C
power_temp_delta* number Power temperature in °C
cpucoolerchart.views.update()[source]

Enqueues an update job so that a worker process update the database. This feature is enabled when USE_QUEUE is True. To process enqueued jobs, run a worker process (cpucoolerchart runworker).

There is a special feature that starts a worker process to reduce the server cost. Only Heroku is supported for now. Set START_WORKER_NODE to "heroku" and HEROKU_API_KEY and HEROKU_APP_NAME to your Heroku API key and app name respectively.

To prevent DDoS attacks, it cannot be requested more frequently than once per 5 minutes when app.debug is False.

Example request:

POST /update HTTP/1.1
Host: example.com
Accept: application/json

Example response:

HTTP/1.1 202 OK
Content-Type: application/json

{
  "msg": "process started"
}

It returns a JSON object containing a message in the msg property. Possible messages are:

message description
process started An update job is enqueued and a worker process has started
too many requests There was a request within the last 5 minutes
already up to date data is already up to date
invalid worker type worker type is other than "heroku"
Heroku API key is not set HEROKU_API_KEY is not set in config
Heroku app name is not set HEROKU_APP_NAME is not set in config
heroku is not installed. Add heroku to your requirements.txt heroku Python module is not installed on your dyno.
failed to enqueue a job an error occurred during enqueuing a job
failed to start a worker an error occurred during starting a worker
Status 202:an update job is enqueued and a worker process has started
Status 404:the app is not configured to update data via HTTP
Status 429:there was a request within the last 5 minutes
Status 500:an error occurred during enqueuing a job or starting a worker
Status 503:worker settings are not valid