├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.rst ├── compile.py ├── dashie.png ├── ez_setup.py ├── pydashie ├── __init__.py ├── assets │ ├── images │ │ ├── dashie.png │ │ └── favicon.ico │ ├── javascripts │ │ ├── app.js │ │ ├── application.coffee │ │ ├── application.js │ │ ├── batman.jquery.js │ │ ├── batman.js │ │ ├── d3.v2.min.js │ │ ├── dashing.coffee │ │ ├── dashing.gridster.coffee │ │ ├── es5-shim.js │ │ ├── jquery.gridster.js │ │ ├── jquery.js │ │ ├── jquery.knob.js │ │ ├── jquery.leanModal.min.js │ │ └── rickshaw.min.js │ └── stylesheets │ │ ├── application.css │ │ ├── application.css~ │ │ ├── application.scss │ │ ├── font-awesome.css │ │ └── jquery.gridster.css ├── dashie_sampler.py ├── example_app.py ├── example_samplers.py ├── main.py ├── repeated_timer.py ├── samplers │ ├── __init__.py │ ├── trello_sampler.py │ └── website_up.py ├── templates │ ├── main.html │ └── small.html └── widgets │ ├── clock │ ├── clock.coffee │ ├── clock.html │ └── clock.scss │ ├── comments │ ├── comments.coffee │ ├── comments.html │ └── comments.scss │ ├── graph │ ├── graph.coffee │ ├── graph.html │ └── graph.scss │ ├── iframe │ ├── iframe.coffee │ ├── iframe.html │ └── iframe.scss │ ├── image │ ├── image.coffee │ ├── image.html │ └── image.scss │ ├── list │ ├── list.coffee │ ├── list.html │ └── list.scss │ ├── meter │ ├── meter.coffee │ ├── meter.html │ └── meter.scss │ ├── number │ ├── number.coffee │ ├── number.html │ └── number.scss │ └── text │ ├── text.coffee │ ├── text.html │ └── text.scss ├── requirements.txt └── setup.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .project.txt 2 | .pydevproject.txt 3 | *.pyc 4 | *Thumbs.db -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Stephen Brown 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | PyDashie 2 | ######## 3 | 4 | This project is mostly dormant now. 5 | 6 | `Main Documentation `_ 7 | 8 | PyDashy is a port of `Dashing `_ by `Shopify `_ to Python 2.7 9 | 10 | .. image:: http://evolvedlight.github.com/pydashie/images/mainscreen.png 11 | 12 | Installation 13 | ############ 14 | 15 | For development purposes, 16 | 17 | python setup.py develop 18 | 19 | OR 20 | 21 | python setup.py install 22 | 23 | And you can run the application by 24 | 25 | pydashie 26 | 27 | Goto localhost:5000 to view the sample application in action. 28 | -------------------------------------------------------------------------------- /compile.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import logging 4 | import shutil 5 | import subprocess 6 | from scss import Scss 7 | 8 | log = logging.getLogger('PydashieCompiler') 9 | logging.basicConfig() 10 | log.setLevel(logging.INFO) 11 | #Requirements: 12 | 13 | #pip install pyScss 14 | 15 | 16 | # 17 | def main(): 18 | #Options 19 | refreshDashingCode=True 20 | 21 | 22 | #Check out dashing into temp directory 23 | current_directory = os.getcwd() 24 | 25 | tmp_dir = os.path.join(current_directory, 'tmp') 26 | if not os.path.exists(tmp_dir): 27 | log.info('Creating tmp dir %s' % tmp_dir) 28 | os.mkdir(tmp_dir) 29 | 30 | tmp_build_dir = os.path.join(tmp_dir, 'bin') 31 | if not os.path.exists(tmp_build_dir): 32 | log.info('Creating bin dir %s' % tmp_build_dir) 33 | os.mkdir(tmp_build_dir) 34 | 35 | dashing_dir = os.path.join(current_directory, 'tmp', 'dashing') 36 | if refreshDashingCode: 37 | if os.path.exists(dashing_dir): 38 | log.info('Removing old Dashing Clone') 39 | shutil.rmtree(dashing_dir) 40 | 41 | log.info('Creating dashing tmp dir %s' % dashing_dir) 42 | os.mkdir(dashing_dir) 43 | 44 | os.chdir(dashing_dir) 45 | if refreshDashingCode: 46 | log.info('Cloning Dashing Code') 47 | subprocess.call("git clone https://github.com/Shopify/dashing", shell=True) 48 | fileList = [] 49 | for root, subFolders, files in os.walk(dashing_dir): 50 | for fileName in files: 51 | if 'scss' in fileName: 52 | fileList.append(os.path.join(root, fileName)) 53 | log.info('Found SCSS to compile: %s' % fileName) 54 | import StringIO 55 | css_output = StringIO.StringIO() 56 | css = Scss() 57 | css_output.write('\n'.join([css.compile(open(filePath).read()) for filePath in fileList])) 58 | 59 | fileList = [] 60 | for root, subFolders, files in os.walk(dashing_dir): 61 | for fileName in files: 62 | if 'css' in fileName and 'scss' not in fileName: 63 | fileList.append(os.path.join(root, fileName)) 64 | log.info('Found CSS to append: %s' % fileName) 65 | css_output.write('\n'.join([open(filePath).read() for filePath in fileList])) 66 | 67 | with open(os.path.join(tmp_build_dir, 'application.css'), 'w') as outfile: 68 | outfile.write(css_output.getvalue()) 69 | log.info('Wrote CSS out') 70 | 71 | if __name__ == '__main__': 72 | main() -------------------------------------------------------------------------------- /dashie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvedlight/pydashie/b93f799596b0e86ea970b26f3a7bf5415bb01e55/dashie.png -------------------------------------------------------------------------------- /ez_setup.py: -------------------------------------------------------------------------------- 1 | #!python 2 | """Bootstrap distribute installation 3 | 4 | If you want to use setuptools in your package's setup.py, just include this 5 | file in the same directory with it, and add this to the top of your setup.py:: 6 | 7 | from distribute_setup import use_setuptools 8 | use_setuptools() 9 | 10 | If you want to require a specific version of setuptools, set a download 11 | mirror, or use an alternate download directory, you can do so by supplying 12 | the appropriate options to ``use_setuptools()``. 13 | 14 | This file can also be run as a script to install or upgrade setuptools. 15 | """ 16 | import os 17 | import sys 18 | import time 19 | import fnmatch 20 | import tempfile 21 | import tarfile 22 | from distutils import log 23 | 24 | try: 25 | from site import USER_SITE 26 | except ImportError: 27 | USER_SITE = None 28 | 29 | try: 30 | import subprocess 31 | 32 | def _python_cmd(*args): 33 | args = (sys.executable,) + args 34 | return subprocess.call(args) == 0 35 | 36 | except ImportError: 37 | # will be used for python 2.3 38 | def _python_cmd(*args): 39 | args = (sys.executable,) + args 40 | # quoting arguments if windows 41 | if sys.platform == 'win32': 42 | def quote(arg): 43 | if ' ' in arg: 44 | return '"%s"' % arg 45 | return arg 46 | args = [quote(arg) for arg in args] 47 | return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 48 | 49 | DEFAULT_VERSION = "0.6.14" 50 | DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" 51 | SETUPTOOLS_FAKED_VERSION = "0.6c11" 52 | 53 | SETUPTOOLS_PKG_INFO = """\ 54 | Metadata-Version: 1.0 55 | Name: setuptools 56 | Version: %s 57 | Summary: xxxx 58 | Home-page: xxx 59 | Author: xxx 60 | Author-email: xxx 61 | License: xxx 62 | Description: xxx 63 | """ % SETUPTOOLS_FAKED_VERSION 64 | 65 | 66 | def _install(tarball): 67 | # extracting the tarball 68 | tmpdir = tempfile.mkdtemp() 69 | log.warn('Extracting in %s', tmpdir) 70 | old_wd = os.getcwd() 71 | try: 72 | os.chdir(tmpdir) 73 | tar = tarfile.open(tarball) 74 | _extractall(tar) 75 | tar.close() 76 | 77 | # going in the directory 78 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 79 | os.chdir(subdir) 80 | log.warn('Now working in %s', subdir) 81 | 82 | # installing 83 | log.warn('Installing Distribute') 84 | if not _python_cmd('setup.py', 'install'): 85 | log.warn('Something went wrong during the installation.') 86 | log.warn('See the error message above.') 87 | finally: 88 | os.chdir(old_wd) 89 | 90 | 91 | def _build_egg(egg, tarball, to_dir): 92 | # extracting the tarball 93 | tmpdir = tempfile.mkdtemp() 94 | log.warn('Extracting in %s', tmpdir) 95 | old_wd = os.getcwd() 96 | try: 97 | os.chdir(tmpdir) 98 | tar = tarfile.open(tarball) 99 | _extractall(tar) 100 | tar.close() 101 | 102 | # going in the directory 103 | subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) 104 | os.chdir(subdir) 105 | log.warn('Now working in %s', subdir) 106 | 107 | # building an egg 108 | log.warn('Building a Distribute egg in %s', to_dir) 109 | _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) 110 | 111 | finally: 112 | os.chdir(old_wd) 113 | # returning the result 114 | log.warn(egg) 115 | if not os.path.exists(egg): 116 | raise IOError('Could not build the egg.') 117 | 118 | 119 | def _do_download(version, download_base, to_dir, download_delay): 120 | egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' 121 | % (version, sys.version_info[0], sys.version_info[1])) 122 | if not os.path.exists(egg): 123 | tarball = download_setuptools(version, download_base, 124 | to_dir, download_delay) 125 | _build_egg(egg, tarball, to_dir) 126 | sys.path.insert(0, egg) 127 | import setuptools 128 | setuptools.bootstrap_install_from = egg 129 | 130 | 131 | def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 132 | to_dir=os.curdir, download_delay=15, no_fake=True): 133 | # making sure we use the absolute path 134 | to_dir = os.path.abspath(to_dir) 135 | was_imported = 'pkg_resources' in sys.modules or \ 136 | 'setuptools' in sys.modules 137 | try: 138 | try: 139 | import pkg_resources 140 | if not hasattr(pkg_resources, '_distribute'): 141 | if not no_fake: 142 | _fake_setuptools() 143 | raise ImportError 144 | except ImportError: 145 | return _do_download(version, download_base, to_dir, download_delay) 146 | try: 147 | pkg_resources.require("distribute>="+version) 148 | return 149 | except pkg_resources.VersionConflict: 150 | e = sys.exc_info()[1] 151 | if was_imported: 152 | sys.stderr.write( 153 | "The required version of distribute (>=%s) is not available,\n" 154 | "and can't be installed while this script is running. Please\n" 155 | "install a more recent version first, using\n" 156 | "'easy_install -U distribute'." 157 | "\n\n(Currently using %r)\n" % (version, e.args[0])) 158 | sys.exit(2) 159 | else: 160 | del pkg_resources, sys.modules['pkg_resources'] # reload ok 161 | return _do_download(version, download_base, to_dir, 162 | download_delay) 163 | except pkg_resources.DistributionNotFound: 164 | return _do_download(version, download_base, to_dir, 165 | download_delay) 166 | finally: 167 | if not no_fake: 168 | _create_fake_setuptools_pkg_info(to_dir) 169 | 170 | def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, 171 | to_dir=os.curdir, delay=15): 172 | """Download distribute from a specified location and return its filename 173 | 174 | `version` should be a valid distribute version number that is available 175 | as an egg for download under the `download_base` URL (which should end 176 | with a '/'). `to_dir` is the directory where the egg will be downloaded. 177 | `delay` is the number of seconds to pause before an actual download 178 | attempt. 179 | """ 180 | # making sure we use the absolute path 181 | to_dir = os.path.abspath(to_dir) 182 | try: 183 | from urllib.request import urlopen 184 | except ImportError: 185 | from urllib2 import urlopen 186 | tgz_name = "distribute-%s.tar.gz" % version 187 | url = download_base + tgz_name 188 | saveto = os.path.join(to_dir, tgz_name) 189 | src = dst = None 190 | if not os.path.exists(saveto): # Avoid repeated downloads 191 | try: 192 | log.warn("Downloading %s", url) 193 | src = urlopen(url) 194 | # Read/write all in one block, so we don't create a corrupt file 195 | # if the download is interrupted. 196 | data = src.read() 197 | dst = open(saveto, "wb") 198 | dst.write(data) 199 | finally: 200 | if src: 201 | src.close() 202 | if dst: 203 | dst.close() 204 | return os.path.realpath(saveto) 205 | 206 | def _no_sandbox(function): 207 | def __no_sandbox(*args, **kw): 208 | try: 209 | from setuptools.sandbox import DirectorySandbox 210 | if not hasattr(DirectorySandbox, '_old'): 211 | def violation(*args): 212 | pass 213 | DirectorySandbox._old = DirectorySandbox._violation 214 | DirectorySandbox._violation = violation 215 | patched = True 216 | else: 217 | patched = False 218 | except ImportError: 219 | patched = False 220 | 221 | try: 222 | return function(*args, **kw) 223 | finally: 224 | if patched: 225 | DirectorySandbox._violation = DirectorySandbox._old 226 | del DirectorySandbox._old 227 | 228 | return __no_sandbox 229 | 230 | def _patch_file(path, content): 231 | """Will backup the file then patch it""" 232 | existing_content = open(path).read() 233 | if existing_content == content: 234 | # already patched 235 | log.warn('Already patched.') 236 | return False 237 | log.warn('Patching...') 238 | _rename_path(path) 239 | f = open(path, 'w') 240 | try: 241 | f.write(content) 242 | finally: 243 | f.close() 244 | return True 245 | 246 | _patch_file = _no_sandbox(_patch_file) 247 | 248 | def _same_content(path, content): 249 | return open(path).read() == content 250 | 251 | def _rename_path(path): 252 | new_name = path + '.OLD.%s' % time.time() 253 | log.warn('Renaming %s into %s', path, new_name) 254 | os.rename(path, new_name) 255 | return new_name 256 | 257 | def _remove_flat_installation(placeholder): 258 | if not os.path.isdir(placeholder): 259 | log.warn('Unkown installation at %s', placeholder) 260 | return False 261 | found = False 262 | for file in os.listdir(placeholder): 263 | if fnmatch.fnmatch(file, 'setuptools*.egg-info'): 264 | found = True 265 | break 266 | if not found: 267 | log.warn('Could not locate setuptools*.egg-info') 268 | return 269 | 270 | log.warn('Removing elements out of the way...') 271 | pkg_info = os.path.join(placeholder, file) 272 | if os.path.isdir(pkg_info): 273 | patched = _patch_egg_dir(pkg_info) 274 | else: 275 | patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) 276 | 277 | if not patched: 278 | log.warn('%s already patched.', pkg_info) 279 | return False 280 | # now let's move the files out of the way 281 | for element in ('setuptools', 'pkg_resources.py', 'site.py'): 282 | element = os.path.join(placeholder, element) 283 | if os.path.exists(element): 284 | _rename_path(element) 285 | else: 286 | log.warn('Could not find the %s element of the ' 287 | 'Setuptools distribution', element) 288 | return True 289 | 290 | _remove_flat_installation = _no_sandbox(_remove_flat_installation) 291 | 292 | def _after_install(dist): 293 | log.warn('After install bootstrap.') 294 | placeholder = dist.get_command_obj('install').install_purelib 295 | _create_fake_setuptools_pkg_info(placeholder) 296 | 297 | def _create_fake_setuptools_pkg_info(placeholder): 298 | if not placeholder or not os.path.exists(placeholder): 299 | log.warn('Could not find the install location') 300 | return 301 | pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) 302 | setuptools_file = 'setuptools-%s-py%s.egg-info' % \ 303 | (SETUPTOOLS_FAKED_VERSION, pyver) 304 | pkg_info = os.path.join(placeholder, setuptools_file) 305 | if os.path.exists(pkg_info): 306 | log.warn('%s already exists', pkg_info) 307 | return 308 | 309 | log.warn('Creating %s', pkg_info) 310 | f = open(pkg_info, 'w') 311 | try: 312 | f.write(SETUPTOOLS_PKG_INFO) 313 | finally: 314 | f.close() 315 | 316 | pth_file = os.path.join(placeholder, 'setuptools.pth') 317 | log.warn('Creating %s', pth_file) 318 | f = open(pth_file, 'w') 319 | try: 320 | f.write(os.path.join(os.curdir, setuptools_file)) 321 | finally: 322 | f.close() 323 | 324 | _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) 325 | 326 | def _patch_egg_dir(path): 327 | # let's check if it's already patched 328 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 329 | if os.path.exists(pkg_info): 330 | if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): 331 | log.warn('%s already patched.', pkg_info) 332 | return False 333 | _rename_path(path) 334 | os.mkdir(path) 335 | os.mkdir(os.path.join(path, 'EGG-INFO')) 336 | pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') 337 | f = open(pkg_info, 'w') 338 | try: 339 | f.write(SETUPTOOLS_PKG_INFO) 340 | finally: 341 | f.close() 342 | return True 343 | 344 | _patch_egg_dir = _no_sandbox(_patch_egg_dir) 345 | 346 | def _before_install(): 347 | log.warn('Before install bootstrap.') 348 | _fake_setuptools() 349 | 350 | 351 | def _under_prefix(location): 352 | if 'install' not in sys.argv: 353 | return True 354 | args = sys.argv[sys.argv.index('install')+1:] 355 | for index, arg in enumerate(args): 356 | for option in ('--root', '--prefix'): 357 | if arg.startswith('%s=' % option): 358 | top_dir = arg.split('root=')[-1] 359 | return location.startswith(top_dir) 360 | elif arg == option: 361 | if len(args) > index: 362 | top_dir = args[index+1] 363 | return location.startswith(top_dir) 364 | if arg == '--user' and USER_SITE is not None: 365 | return location.startswith(USER_SITE) 366 | return True 367 | 368 | 369 | def _fake_setuptools(): 370 | log.warn('Scanning installed packages') 371 | try: 372 | import pkg_resources 373 | except ImportError: 374 | # we're cool 375 | log.warn('Setuptools or Distribute does not seem to be installed.') 376 | return 377 | ws = pkg_resources.working_set 378 | try: 379 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', 380 | replacement=False)) 381 | except TypeError: 382 | # old distribute API 383 | setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) 384 | 385 | if setuptools_dist is None: 386 | log.warn('No setuptools distribution found') 387 | return 388 | # detecting if it was already faked 389 | setuptools_location = setuptools_dist.location 390 | log.warn('Setuptools installation detected at %s', setuptools_location) 391 | 392 | # if --root or --preix was provided, and if 393 | # setuptools is not located in them, we don't patch it 394 | if not _under_prefix(setuptools_location): 395 | log.warn('Not patching, --root or --prefix is installing Distribute' 396 | ' in another location') 397 | return 398 | 399 | # let's see if its an egg 400 | if not setuptools_location.endswith('.egg'): 401 | log.warn('Non-egg installation') 402 | res = _remove_flat_installation(setuptools_location) 403 | if not res: 404 | return 405 | else: 406 | log.warn('Egg installation') 407 | pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') 408 | if (os.path.exists(pkg_info) and 409 | _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): 410 | log.warn('Already patched.') 411 | return 412 | log.warn('Patching...') 413 | # let's create a fake egg replacing setuptools one 414 | res = _patch_egg_dir(setuptools_location) 415 | if not res: 416 | return 417 | log.warn('Patched done.') 418 | _relaunch() 419 | 420 | 421 | def _relaunch(): 422 | log.warn('Relaunching...') 423 | # we have to relaunch the process 424 | # pip marker to avoid a relaunch bug 425 | if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: 426 | sys.argv[0] = 'setup.py' 427 | args = [sys.executable] + sys.argv 428 | sys.exit(subprocess.call(args)) 429 | 430 | 431 | def _extractall(self, path=".", members=None): 432 | """Extract all members from the archive to the current working 433 | directory and set owner, modification time and permissions on 434 | directories afterwards. `path' specifies a different directory 435 | to extract to. `members' is optional and must be a subset of the 436 | list returned by getmembers(). 437 | """ 438 | import copy 439 | import operator 440 | from tarfile import ExtractError 441 | directories = [] 442 | 443 | if members is None: 444 | members = self 445 | 446 | for tarinfo in members: 447 | if tarinfo.isdir(): 448 | # Extract directories with a safe mode. 449 | directories.append(tarinfo) 450 | tarinfo = copy.copy(tarinfo) 451 | tarinfo.mode = 448 # decimal for oct 0700 452 | self.extract(tarinfo, path) 453 | 454 | # Reverse sort directories. 455 | if sys.version_info < (2, 4): 456 | def sorter(dir1, dir2): 457 | return cmp(dir1.name, dir2.name) 458 | directories.sort(sorter) 459 | directories.reverse() 460 | else: 461 | directories.sort(key=operator.attrgetter('name'), reverse=True) 462 | 463 | # Set correct owner, mtime and filemode on directories. 464 | for tarinfo in directories: 465 | dirpath = os.path.join(path, tarinfo.name) 466 | try: 467 | self.chown(tarinfo, dirpath) 468 | self.utime(tarinfo, dirpath) 469 | self.chmod(tarinfo, dirpath) 470 | except ExtractError: 471 | e = sys.exc_info()[1] 472 | if self.errorlevel > 1: 473 | raise 474 | else: 475 | self._dbg(1, "tarfile: %s" % e) 476 | 477 | 478 | def main(argv, version=DEFAULT_VERSION): 479 | """Install or upgrade setuptools and EasyInstall""" 480 | tarball = download_setuptools() 481 | _install(tarball) 482 | 483 | 484 | if __name__ == '__main__': 485 | main(sys.argv[1:]) 486 | -------------------------------------------------------------------------------- /pydashie/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvedlight/pydashie/b93f799596b0e86ea970b26f3a7bf5415bb01e55/pydashie/__init__.py -------------------------------------------------------------------------------- /pydashie/assets/images/dashie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvedlight/pydashie/b93f799596b0e86ea970b26f3a7bf5415bb01e55/pydashie/assets/images/dashie.png -------------------------------------------------------------------------------- /pydashie/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evolvedlight/pydashie/b93f799596b0e86ea970b26f3a7bf5415bb01e55/pydashie/assets/images/favicon.ico -------------------------------------------------------------------------------- /pydashie/assets/javascripts/app.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | Dashing.Clock = (function(_super) { 7 | 8 | __extends(Clock, _super); 9 | 10 | function Clock() { 11 | this.startTime = __bind(this.startTime, this); 12 | return Clock.__super__.constructor.apply(this, arguments); 13 | } 14 | 15 | Clock.prototype.ready = function() { 16 | return setInterval(this.startTime, 500); 17 | }; 18 | 19 | Clock.prototype.startTime = function() { 20 | var h, m, s, today; 21 | today = new Date(); 22 | h = today.getHours(); 23 | m = today.getMinutes(); 24 | s = today.getSeconds(); 25 | m = this.formatTime(m); 26 | s = this.formatTime(s); 27 | this.set('time', h + ":" + m + ":" + s); 28 | return this.set('date', today.toDateString()); 29 | }; 30 | 31 | Clock.prototype.formatTime = function(i) { 32 | if (i < 10) { 33 | return "0" + i; 34 | } else { 35 | return i; 36 | } 37 | }; 38 | 39 | return Clock; 40 | 41 | })(Dashing.Widget); 42 | 43 | }).call(this); 44 | (function() { 45 | var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 46 | __hasProp = {}.hasOwnProperty, 47 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 48 | 49 | Dashing.Comments = (function(_super) { 50 | 51 | __extends(Comments, _super); 52 | 53 | function Comments() { 54 | this.nextComment = __bind(this.nextComment, this); 55 | return Comments.__super__.constructor.apply(this, arguments); 56 | } 57 | 58 | Comments.accessor('quote', function() { 59 | var _ref; 60 | return "“" + ((_ref = this.get('current_comment')) != null ? _ref.body : void 0) + "”"; 61 | }); 62 | 63 | Comments.prototype.ready = function() { 64 | this.currentIndex = 0; 65 | this.commentElem = $(this.node).find('.comment-container'); 66 | this.nextComment(); 67 | return this.startCarousel(); 68 | }; 69 | 70 | Comments.prototype.onData = function(data) { 71 | return this.currentIndex = 0; 72 | }; 73 | 74 | Comments.prototype.startCarousel = function() { 75 | return setInterval(this.nextComment, 8000); 76 | }; 77 | 78 | Comments.prototype.nextComment = function() { 79 | var comments, 80 | _this = this; 81 | comments = this.get('comments'); 82 | if (comments) { 83 | return this.commentElem.fadeOut(function() { 84 | _this.currentIndex = (_this.currentIndex + 1) % comments.length; 85 | _this.set('current_comment', comments[_this.currentIndex]); 86 | return _this.commentElem.fadeIn(); 87 | }); 88 | } 89 | }; 90 | 91 | return Comments; 92 | 93 | })(Dashing.Widget); 94 | 95 | }).call(this); 96 | (function() { 97 | var __hasProp = {}.hasOwnProperty, 98 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 99 | 100 | Dashing.Graph = (function(_super) { 101 | 102 | __extends(Graph, _super); 103 | 104 | function Graph() { 105 | return Graph.__super__.constructor.apply(this, arguments); 106 | } 107 | 108 | Graph.accessor('current', function() { 109 | var points; 110 | if (this.get('displayedValue')) { 111 | return this.get('displayedValue'); 112 | } 113 | points = this.get('points'); 114 | if (points) { 115 | return points[points.length - 1].y; 116 | } 117 | }); 118 | 119 | Graph.prototype.ready = function() { 120 | var container, height, width, x_axis, y_axis; 121 | container = $(this.node).parent(); 122 | width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1); 123 | height = Dashing.widget_base_dimensions[1] * container.data("sizey"); 124 | this.graph = new Rickshaw.Graph({ 125 | element: this.node, 126 | width: width, 127 | height: height, 128 | series: [ 129 | { 130 | color: "#fff", 131 | data: [ 132 | { 133 | x: 0, 134 | y: 0 135 | } 136 | ] 137 | } 138 | ] 139 | }); 140 | if (this.get('points')) { 141 | this.graph.series[0].data = this.get('points'); 142 | } 143 | x_axis = new Rickshaw.Graph.Axis.Time({ 144 | graph: this.graph 145 | }); 146 | y_axis = new Rickshaw.Graph.Axis.Y({ 147 | graph: this.graph, 148 | tickFormat: Rickshaw.Fixtures.Number.formatKMBT 149 | }); 150 | return this.graph.render(); 151 | }; 152 | 153 | Graph.prototype.onData = function(data) { 154 | if (this.graph) { 155 | this.graph.series[0].data = data.points; 156 | return this.graph.render(); 157 | } 158 | }; 159 | 160 | return Graph; 161 | 162 | })(Dashing.Widget); 163 | 164 | }).call(this); 165 | (function() { 166 | var __hasProp = {}.hasOwnProperty, 167 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 168 | 169 | Dashing.Iframe = (function(_super) { 170 | 171 | __extends(Iframe, _super); 172 | 173 | function Iframe() { 174 | return Iframe.__super__.constructor.apply(this, arguments); 175 | } 176 | 177 | Iframe.prototype.ready = function() {}; 178 | 179 | Iframe.prototype.onData = function(data) {}; 180 | 181 | return Iframe; 182 | 183 | })(Dashing.Widget); 184 | 185 | }).call(this); 186 | (function() { 187 | var __hasProp = {}.hasOwnProperty, 188 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 189 | 190 | Dashing.Image = (function(_super) { 191 | 192 | __extends(Image, _super); 193 | 194 | function Image() { 195 | return Image.__super__.constructor.apply(this, arguments); 196 | } 197 | 198 | Image.prototype.ready = function() {}; 199 | 200 | Image.prototype.onData = function(data) {}; 201 | 202 | return Image; 203 | 204 | })(Dashing.Widget); 205 | 206 | }).call(this); 207 | (function() { 208 | var __hasProp = {}.hasOwnProperty, 209 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 210 | 211 | Dashing.List = (function(_super) { 212 | 213 | __extends(List, _super); 214 | 215 | function List() { 216 | return List.__super__.constructor.apply(this, arguments); 217 | } 218 | 219 | List.prototype.ready = function() { 220 | if (this.get('unordered')) { 221 | return $(this.node).find('ol').remove(); 222 | } else { 223 | return $(this.node).find('ul').remove(); 224 | } 225 | }; 226 | 227 | return List; 228 | 229 | })(Dashing.Widget); 230 | 231 | }).call(this); 232 | (function() { 233 | var __hasProp = {}.hasOwnProperty, 234 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 235 | 236 | Dashing.Meter = (function(_super) { 237 | 238 | __extends(Meter, _super); 239 | 240 | Meter.accessor('value', Dashing.AnimatedValue); 241 | 242 | function Meter() { 243 | Meter.__super__.constructor.apply(this, arguments); 244 | this.observe('value', function(value) { 245 | return $(this.node).find(".meter").val(value).trigger('change'); 246 | }); 247 | } 248 | 249 | Meter.prototype.ready = function() { 250 | var meter; 251 | meter = $(this.node).find(".meter"); 252 | meter.attr("data-bgcolor", meter.css("background-color")); 253 | meter.attr("data-fgcolor", meter.css("color")); 254 | return meter.knob(); 255 | }; 256 | 257 | return Meter; 258 | 259 | })(Dashing.Widget); 260 | 261 | }).call(this); 262 | (function() { 263 | var __hasProp = {}.hasOwnProperty, 264 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 265 | 266 | Dashing.Number = (function(_super) { 267 | 268 | __extends(Number, _super); 269 | 270 | function Number() { 271 | return Number.__super__.constructor.apply(this, arguments); 272 | } 273 | 274 | Number.accessor('current', Dashing.AnimatedValue); 275 | 276 | Number.accessor('difference', function() { 277 | var current, diff, last; 278 | if (this.get('last')) { 279 | last = parseInt(this.get('last')); 280 | current = parseInt(this.get('current')); 281 | if (last !== 0) { 282 | diff = Math.abs(Math.round((current - last) / last * 100)); 283 | return "" + diff + "%"; 284 | } 285 | } else { 286 | return ""; 287 | } 288 | }); 289 | 290 | Number.accessor('arrow', function() { 291 | if (this.get('last')) { 292 | if (parseInt(this.get('current')) > parseInt(this.get('last'))) { 293 | return 'icon-arrow-up'; 294 | } else { 295 | return 'icon-arrow-down'; 296 | } 297 | } 298 | }); 299 | 300 | Number.accessor('needsAttention', function() { 301 | return this.get('status') === 'warning' || this.get('status') === 'danger'; 302 | }); 303 | 304 | Number.prototype.onData = function(data) { 305 | if (data.status) { 306 | return $(this.get('node')).addClass("status-" + data.status); 307 | } 308 | }; 309 | 310 | return Number; 311 | 312 | })(Dashing.Widget); 313 | 314 | }).call(this); 315 | (function() { 316 | var __hasProp = {}.hasOwnProperty, 317 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 318 | 319 | Dashing.Text = (function(_super) { 320 | 321 | __extends(Text, _super); 322 | 323 | function Text() { 324 | return Text.__super__.constructor.apply(this, arguments); 325 | } 326 | 327 | return Text; 328 | 329 | })(Dashing.Widget); 330 | 331 | }).call(this); 332 | (function() { 333 | 334 | console.log("Yeah! The dashboard has started!"); 335 | 336 | Dashing.on('ready', function() { 337 | var contentWidth; 338 | Dashing.widget_margins || (Dashing.widget_margins = [5, 5]); 339 | Dashing.widget_base_dimensions || (Dashing.widget_base_dimensions = [300, 360]); 340 | Dashing.numColumns || (Dashing.numColumns = 4); 341 | contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns; 342 | return Batman.setImmediate(function() { 343 | $('.gridster').width(contentWidth); 344 | return $('.gridster ul:first').gridster({ 345 | widget_margins: Dashing.widget_margins, 346 | widget_base_dimensions: Dashing.widget_base_dimensions, 347 | avoid_overlapped_widgets: !Dashing.customGridsterLayout, 348 | draggable: { 349 | stop: Dashing.showGridsterInstructions 350 | } 351 | }); 352 | }); 353 | }); 354 | 355 | }).call(this); 356 | -------------------------------------------------------------------------------- /pydashie/assets/javascripts/application.coffee: -------------------------------------------------------------------------------- 1 | console.log("Yeah! The dashboard has started!") 2 | 3 | Dashing.on 'ready', -> 4 | Dashing.widget_margins ||= [5, 5] 5 | Dashing.widget_base_dimensions ||= [300, 360] 6 | Dashing.numColumns ||= 4 7 | 8 | contentWidth = (Dashing.widget_base_dimensions[0] + Dashing.widget_margins[0] * 2) * Dashing.numColumns 9 | 10 | Batman.setImmediate -> 11 | $('.gridster').width(contentWidth) 12 | $('.gridster ul:first').gridster 13 | widget_margins: Dashing.widget_margins 14 | widget_base_dimensions: Dashing.widget_base_dimensions 15 | avoid_overlapped_widgets: !Dashing.customGridsterLayout 16 | draggable: 17 | stop: Dashing.showGridsterInstructions 18 | start: -> Dashing.currentWidgetPositions = Dashing.getWidgetPositions() 19 | -------------------------------------------------------------------------------- /pydashie/assets/javascripts/dashing.coffee: -------------------------------------------------------------------------------- 1 | #= require jquery 2 | #= require es5-shim 3 | #= require batman 4 | #= require batman.jquery 5 | 6 | 7 | #Batman.Filters.prettyNumber = (num) -> 8 | # num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") unless isNaN(num) 9 | # 10 | #Batman.Filters.dashize = (str) -> 11 | # dashes_rx1 = /([A-Z]+)([A-Z][a-z])/g; 12 | # dashes_rx2 = /([a-z\d])([A-Z])/g; 13 | # 14 | # return str.replace(dashes_rx1, '$1_$2').replace(dashes_rx2, '$1_$2').replace('_', '-').toLowerCase() 15 | # 16 | #Batman.Filters.shortenedNumber = (num) -> 17 | # return num if isNaN(num) 18 | # if num >= 1000000000 19 | # (num / 1000000000).toFixed(1) + 'B' 20 | # else if num >= 1000000 21 | # (num / 1000000).toFixed(1) + 'M' 22 | # else if num >= 1000 23 | # (num / 1000).toFixed(1) + 'K' 24 | # else 25 | # num 26 | 27 | #class window.Dashing extends Batman.App 28 | # @root -> 29 | #Dashing.params = Batman.URI.paramsFromQuery(window.location.search.slice(1)); 30 | # 31 | #class Dashing.Widget extends Batman.View 32 | # constructor: -> 33 | # # Set the view path 34 | # @constructor::source = Batman.Filters.underscore(@constructor.name) 35 | # super 36 | # 37 | # @mixin($(@node).data()) 38 | # Dashing.widgets[@id] ||= [] 39 | # Dashing.widgets[@id].push(@) 40 | # @mixin(Dashing.lastEvents[@id]) # in case the events from the server came before the widget was rendered 41 | # 42 | # type = Batman.Filters.dashize(@view) 43 | # $(@node).addClass("widget widget-#{type} #{@id}") 44 | # 45 | # @accessor 'updatedAtMessage', -> 46 | # if updatedAt = @get('updatedAt') 47 | # timestamp = updatedAt.toString().match(/\d*:\d*/)[0] 48 | # "Last updated at #{timestamp}" 49 | # 50 | # @::on 'ready', -> 51 | # Dashing.Widget.fire 'ready' 52 | # 53 | # receiveData: (data) => 54 | # @mixin(data) 55 | # @onData(data) 56 | # 57 | # onData: (data) => 58 | # # Widgets override this to handle incoming data 59 | # 60 | 61 | Dashing.AnimatedValue = 62 | get: Batman.Property.defaultAccessor.get 63 | set: (k, to) -> 64 | if !to? || isNaN(to) 65 | @[k] = to 66 | else 67 | timer = "interval_#{k}" 68 | num = if (!isNaN(@[k]) && @[k]?) then @[k] else 0 69 | unless @[timer] || num == to 70 | to = parseFloat(to) 71 | num = parseFloat(num) 72 | up = to > num 73 | num_interval = Math.abs(num - to) / 90 74 | @[timer] = 75 | setInterval => 76 | num = if up then Math.ceil(num+num_interval) else Math.floor(num-num_interval) 77 | if (up && num > to) || (!up && num < to) 78 | num = to 79 | clearInterval(@[timer]) 80 | @[timer] = null 81 | delete @[timer] 82 | @[k] = num 83 | @set k, to 84 | , 10 85 | @[k] = num 86 | 87 | Dashing.widgets = widgets = {} 88 | Dashing.lastEvents = lastEvents = {} 89 | Dashing.debugMode = false 90 | 91 | source = new EventSource('/events') 92 | source.addEventListener 'open', (e) -> 93 | console.log("Connection opened") 94 | 95 | source.addEventListener 'error', (e)-> 96 | console.log("Connection error") 97 | if (e.readyState == EventSource.CLOSED) 98 | console.log("Connection closed") 99 | 100 | source.addEventListener 'message', (e) => 101 | data = JSON.parse(e.data) 102 | if Dashing.debugMode 103 | console.log("Received data for #{data.id}", data) 104 | lastEvents[data.id] = data 105 | if widgets[data.id]?.length > 0 106 | for widget in widgets[data.id] 107 | widget.receiveData(data) 108 | 109 | 110 | $(document).ready -> 111 | Dashing.run() 112 | -------------------------------------------------------------------------------- /pydashie/assets/javascripts/dashing.gridster.coffee: -------------------------------------------------------------------------------- 1 | #= require_directory ./gridster 2 | 3 | # This file enables gridster integration (http://gridster.net/) 4 | # Delete it if you'd rather handle the layout yourself. 5 | # You'll miss out on a lot if you do, but we won't hold it against you. 6 | 7 | Dashing.gridsterLayout = (positions) -> 8 | Dashing.customGridsterLayout = true 9 | positions = positions.replace(/^"|"$/g, '') 10 | positions = $.parseJSON(positions) 11 | widgets = $("[data-row^=]") 12 | for widget, index in widgets 13 | $(widget).attr('data-row', positions[index].row) 14 | $(widget).attr('data-col', positions[index].col) 15 | 16 | Dashing.showGridsterInstructions = -> 17 | data = $(".gridster ul:first").gridster().data('gridster').serialize(); 18 | $('#save-gridster').slideDown(); 19 | return $('#gridster-code').text(" ") 20 | 21 | $ -> 22 | $('#save-gridster').leanModal() 23 | 24 | $('#save-gridster').click -> 25 | $('#save-gridster').slideUp() 26 | -------------------------------------------------------------------------------- /pydashie/assets/javascripts/es5-shim.js: -------------------------------------------------------------------------------- 1 | // vim: ts=4 sts=4 sw=4 expandtab 2 | // -- kriskowal Kris Kowal Copyright (C) 2009-2011 MIT License 3 | // -- tlrobinson Tom Robinson Copyright (C) 2009-2010 MIT License (Narwhal Project) 4 | // -- dantman Daniel Friesen Copyright (C) 2010 XXX TODO License or CLA 5 | // -- fschaefer Florian Schäfer Copyright (C) 2010 MIT License 6 | // -- Gozala Irakli Gozalishvili Copyright (C) 2010 MIT License 7 | // -- kitcambridge Kit Cambridge Copyright (C) 2011 MIT License 8 | // -- kossnocorp Sasha Koss XXX TODO License or CLA 9 | // -- bryanforbes Bryan Forbes XXX TODO License or CLA 10 | // -- killdream Quildreen Motta Copyright (C) 2011 MIT Licence 11 | // -- michaelficarra Michael Ficarra Copyright (C) 2011 3-clause BSD License 12 | // -- sharkbrainguy Gerard Paapu Copyright (C) 2011 MIT License 13 | // -- bbqsrc Brendan Molloy XXX TODO License or CLA 14 | // -- iwyg XXX TODO License or CLA 15 | // -- DomenicDenicola Domenic Denicola XXX TODO License or CLA 16 | // -- xavierm02 Montillet Xavier XXX TODO License or CLA 17 | // -- Raynos Raynos XXX TODO License or CLA 18 | // -- samsonjs Sami Samhuri XXX TODO License or CLA 19 | // -- rwldrn Rick Waldron Copyright (C) 2011 MIT License 20 | // -- lexer Alexey Zakharov XXX TODO License or CLA 21 | 22 | /*! 23 | Copyright (c) 2009, 280 North Inc. http://280north.com/ 24 | MIT License. http://github.com/280north/narwhal/blob/master/README.md 25 | */ 26 | 27 | // Module systems magic dance 28 | (function (definition) { 29 | // RequireJS 30 | if (typeof define == "function") { 31 | define(definition); 32 | // CommonJS and 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
    23 |
  • 24 |
    25 |
  • 26 | 27 |
  • 28 |
    29 |
  • 30 | 31 |
  • 32 |
    33 |
  • 34 | 35 |
  • 36 |
    37 |
  • 38 | 39 |
  • 40 |
    41 |
  • 42 | 43 |
  • 44 |
    45 |
  • 46 | 47 |
  • 48 |
    49 |
  • 50 | 51 |
  • 52 |
    53 |
  • 54 | 55 |
  • 56 |
    57 |
  • 58 |
59 |
60 | 61 | 62 | -------------------------------------------------------------------------------- /pydashie/templates/small.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | {{title}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 |
22 |
    23 |
  • 24 |
    25 |
  • 26 | 27 |
  • 28 |
    29 |
  • 30 | 31 |
  • 32 |
    33 |
  • 34 | 35 |
  • 36 |
    37 |
  • 38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /pydashie/widgets/clock/clock.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Clock extends Dashing.Widget 2 | 3 | ready: -> 4 | setInterval(@startTime, 500) 5 | 6 | startTime: => 7 | today = new Date() 8 | 9 | h = (today.getHours() % 13) + 1 10 | m = today.getMinutes() 11 | s = today.getSeconds() 12 | m = @formatTime(m) 13 | s = @formatTime(s) 14 | @set('time', h + ":" + m + ":" + s) 15 | @set('date', today.toDateString()) 16 | 17 | formatTime: (i) -> 18 | if i < 10 then "0" + i else i 19 | -------------------------------------------------------------------------------- /pydashie/widgets/clock/clock.html: -------------------------------------------------------------------------------- 1 |

2 |

3 | -------------------------------------------------------------------------------- /pydashie/widgets/clock/clock.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #dc5945; 5 | 6 | // ---------------------------------------------------------------------------- 7 | // Widget-clock styles 8 | // ---------------------------------------------------------------------------- 9 | .widget-clock { 10 | 11 | background-color: $background-color; 12 | 13 | } 14 | -------------------------------------------------------------------------------- /pydashie/widgets/comments/comments.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Comments extends Dashing.Widget 2 | 3 | @accessor 'quote', -> 4 | "“#{@get('current_comment')?.body}”" 5 | 6 | ready: -> 7 | @currentIndex = 0 8 | @commentElem = $(@node).find('.comment-container') 9 | @nextComment() 10 | @startCarousel() 11 | 12 | onData: (data) -> 13 | @currentIndex = 0 14 | 15 | startCarousel: -> 16 | setInterval(@nextComment, 8000) 17 | 18 | nextComment: => 19 | comments = @get('comments') 20 | if comments 21 | @commentElem.fadeOut => 22 | @currentIndex = (@currentIndex + 1) % comments.length 23 | @set 'current_comment', comments[@currentIndex] 24 | @commentElem.fadeIn() -------------------------------------------------------------------------------- /pydashie/widgets/comments/comments.html: -------------------------------------------------------------------------------- 1 |

2 |
3 |

4 |

5 |
6 | 7 |

-------------------------------------------------------------------------------- /pydashie/widgets/comments/comments.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #eb9c3c; 5 | 6 | $title-color: rgba(255, 255, 255, 0.7); 7 | $moreinfo-color: rgba(255, 255, 255, 0.7); 8 | 9 | // ---------------------------------------------------------------------------- 10 | // Widget-comment styles 11 | // ---------------------------------------------------------------------------- 12 | .widget-comments { 13 | 14 | background-color: $background-color; 15 | 16 | .title { 17 | color: $title-color; 18 | margin-bottom: 15px; 19 | } 20 | 21 | .name { 22 | padding-left: 5px; 23 | } 24 | 25 | .comment-container { 26 | display: none; 27 | } 28 | 29 | .more-info { 30 | color: $moreinfo-color; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /pydashie/widgets/graph/graph.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Graph extends Dashing.Widget 2 | 3 | @accessor 'current', -> 4 | return @get('displayedValue') if @get('displayedValue') 5 | points = @get('points') 6 | if points 7 | points[points.length - 1].y 8 | 9 | ready: -> 10 | container = $(@node).parent() 11 | # Gross hacks. Let's fix this. 12 | width = (Dashing.widget_base_dimensions[0] * container.data("sizex")) + Dashing.widget_margins[0] * 2 * (container.data("sizex") - 1) 13 | height = (Dashing.widget_base_dimensions[1] * container.data("sizey")) 14 | @graph = new Rickshaw.Graph( 15 | element: @node 16 | width: width 17 | height: height 18 | series: [ 19 | { 20 | color: "#fff", 21 | data: [{x:0, y:0}] 22 | } 23 | ] 24 | ) 25 | 26 | @graph.series[0].data = @get('points') if @get('points') 27 | 28 | x_axis = new Rickshaw.Graph.Axis.Time(graph: @graph) 29 | y_axis = new Rickshaw.Graph.Axis.Y(graph: @graph, tickFormat: Rickshaw.Fixtures.Number.formatKMBT) 30 | @graph.render() 31 | 32 | onData: (data) -> 33 | if @graph 34 | @graph.series[0].data = data.points 35 | @graph.render() 36 | -------------------------------------------------------------------------------- /pydashie/widgets/graph/graph.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

-------------------------------------------------------------------------------- /pydashie/widgets/graph/graph.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #dc5945; 5 | 6 | $title-color: rgba(255, 255, 255, 0.7); 7 | $moreinfo-color: rgba(255, 255, 255, 0.3); 8 | $tick-color: rgba(0, 0, 0, 0.4); 9 | 10 | 11 | // ---------------------------------------------------------------------------- 12 | // Widget-graph styles 13 | // ---------------------------------------------------------------------------- 14 | .widget-graph { 15 | 16 | background-color: $background-color; 17 | position: relative; 18 | 19 | 20 | svg { 21 | position: absolute; 22 | opacity: 0.4; 23 | fill-opacity: 0.4; 24 | left: 0px; 25 | top: 0px; 26 | } 27 | 28 | .title, .value { 29 | position: relative; 30 | z-index: 99; 31 | } 32 | 33 | .title { 34 | color: $title-color; 35 | } 36 | 37 | .more-info { 38 | color: $moreinfo-color; 39 | font-weight: 600; 40 | font-size: 20px; 41 | margin-top: 0; 42 | } 43 | 44 | .x_tick { 45 | position: absolute; 46 | bottom: 0; 47 | .title { 48 | font-size: 20px; 49 | color: $tick-color; 50 | opacity: 0.5; 51 | padding-bottom: 3px; 52 | } 53 | } 54 | 55 | .y_ticks { 56 | font-size: 20px; 57 | fill: $tick-color; 58 | fill-opacity: 1; 59 | } 60 | 61 | .domain { 62 | display: none; 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /pydashie/widgets/iframe/iframe.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Iframe extends Dashing.Widget 2 | 3 | ready: -> 4 | # This is fired when the widget is done being rendered 5 | 6 | onData: (data) -> 7 | # Handle incoming data 8 | # You can access the html node of this widget with `@node` 9 | # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in. -------------------------------------------------------------------------------- /pydashie/widgets/iframe/iframe.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pydashie/widgets/iframe/iframe.scss: -------------------------------------------------------------------------------- 1 | .widget-iframe { 2 | padding: 3px 0px 0px 0px !important; 3 | 4 | iframe { 5 | width: 100%; 6 | height: 100%; 7 | } 8 | } -------------------------------------------------------------------------------- /pydashie/widgets/image/image.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Image extends Dashing.Widget 2 | 3 | ready: -> 4 | # This is fired when the widget is done being rendered 5 | 6 | onData: (data) -> 7 | # Handle incoming data 8 | # You can access the html node of this widget with `@node` 9 | # Example: $(@node).fadeOut().fadeIn() will make the node flash each time data comes in. -------------------------------------------------------------------------------- /pydashie/widgets/image/image.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pydashie/widgets/image/image.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #4b4b4b; 5 | 6 | // ---------------------------------------------------------------------------- 7 | // Widget-image styles 8 | // ---------------------------------------------------------------------------- 9 | .widget-image { 10 | 11 | background-color: $background-color; 12 | 13 | } -------------------------------------------------------------------------------- /pydashie/widgets/list/list.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.List extends Dashing.Widget 2 | ready: -> 3 | if @get('unordered') 4 | $(@node).find('ol').remove() 5 | else 6 | $(@node).find('ul').remove() -------------------------------------------------------------------------------- /pydashie/widgets/list/list.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |
    4 |
  1. 5 | 6 | 7 |
  2. 8 |
9 | 10 |
    11 |
  • 12 | 13 | 14 |
  • 15 |
16 | 17 |

18 |

19 | -------------------------------------------------------------------------------- /pydashie/widgets/list/list.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #12b0c5; 5 | $value-color: #fff; 6 | 7 | $title-color: rgba(255, 255, 255, 0.7); 8 | $label-color: rgba(255, 255, 255, 0.7); 9 | $moreinfo-color: rgba(255, 255, 255, 0.7); 10 | 11 | // ---------------------------------------------------------------------------- 12 | // Widget-list styles 13 | // ---------------------------------------------------------------------------- 14 | .widget-list { 15 | 16 | background-color: $background-color; 17 | vertical-align: top; 18 | 19 | .title { 20 | color: $title-color; 21 | } 22 | 23 | ol, ul { 24 | margin: 0 15px; 25 | text-align: left; 26 | color: $label-color; 27 | } 28 | 29 | ol { 30 | list-style-position: inside; 31 | } 32 | 33 | li { 34 | margin-bottom: 5px; 35 | } 36 | 37 | .list-nostyle { 38 | list-style: none; 39 | } 40 | 41 | .label { 42 | color: $label-color; 43 | } 44 | 45 | .value { 46 | float: right; 47 | margin-left: 12px; 48 | font-weight: 600; 49 | color: $value-color; 50 | } 51 | 52 | .updated-at { 53 | color: rgba(0, 0, 0, 0.3); 54 | } 55 | 56 | .more-info { 57 | color: $moreinfo-color; 58 | } 59 | 60 | } -------------------------------------------------------------------------------- /pydashie/widgets/meter/meter.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Meter extends Dashing.Widget 2 | 3 | @accessor 'value', Dashing.AnimatedValue 4 | 5 | constructor: -> 6 | super 7 | @observe 'value', (value) -> 8 | $(@node).find(".meter").val(value).trigger('change') 9 | 10 | ready: -> 11 | meter = $(@node).find(".meter") 12 | meter.attr("data-bgcolor", meter.css("background-color")) 13 | meter.attr("data-fgcolor", meter.css("color")) 14 | meter.knob() 15 | -------------------------------------------------------------------------------- /pydashie/widgets/meter/meter.html: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

-------------------------------------------------------------------------------- /pydashie/widgets/meter/meter.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #9c4274; 5 | 6 | $title-color: rgba(255, 255, 255, 0.7); 7 | $moreinfo-color: rgba(255, 255, 255, 0.3); 8 | 9 | $meter-background: darken($background-color, 15%); 10 | 11 | // ---------------------------------------------------------------------------- 12 | // Widget-meter styles 13 | // ---------------------------------------------------------------------------- 14 | .widget-meter { 15 | 16 | background-color: $background-color; 17 | 18 | input.meter { 19 | background-color: $meter-background; 20 | color: #fff; 21 | } 22 | 23 | .title { 24 | color: $title-color; 25 | } 26 | 27 | .more-info { 28 | color: $moreinfo-color; 29 | } 30 | 31 | .updated-at { 32 | color: rgba(0, 0, 0, 0.3); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /pydashie/widgets/number/number.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Number extends Dashing.Widget 2 | @accessor 'current', Dashing.AnimatedValue 3 | 4 | @accessor 'difference', -> 5 | if @get('last') 6 | last = parseInt(@get('last')) 7 | current = parseInt(@get('current')) 8 | if last != 0 9 | diff = Math.abs(Math.round((current - last) / last * 100)) 10 | "#{diff}%" 11 | else 12 | "" 13 | 14 | @accessor 'arrow', -> 15 | if @get('last') 16 | if parseInt(@get('current')) > parseInt(@get('last')) then '+' else '-' 17 | #if parseInt(@get('current')) > parseInt(@get('last')) then 'icon-arrow-up' else 'icon-arrow-down' 18 | 19 | onData: (data) -> 20 | if data.status 21 | $(@get('node')).addClass("status-#{data.status}") -------------------------------------------------------------------------------- /pydashie/widgets/number/number.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 |

8 | 9 |

10 | 11 |

12 | -------------------------------------------------------------------------------- /pydashie/widgets/number/number.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #47bbb3; 5 | $value-color: #fff; 6 | 7 | $title-color: rgba(255, 255, 255, 0.7);; 8 | $moreinfo-color: rgba(255, 255, 255, 0.7);; 9 | 10 | // ---------------------------------------------------------------------------- 11 | // Widget-number styles 12 | // ---------------------------------------------------------------------------- 13 | .widget-number { 14 | 15 | background-color: $background-color; 16 | 17 | .title { 18 | color: $title-color; 19 | } 20 | 21 | .value { 22 | color: $value-color; 23 | } 24 | 25 | .change-rate { 26 | font-weight: 500; 27 | font-size: 30px; 28 | color: $value-color; 29 | } 30 | 31 | .more-info { 32 | color: $moreinfo-color; 33 | } 34 | 35 | .updated-at { 36 | color: rgba(0, 0, 0, 0.3); 37 | } 38 | 39 | } -------------------------------------------------------------------------------- /pydashie/widgets/text/text.coffee: -------------------------------------------------------------------------------- 1 | class Dashing.Text extends Dashing.Widget -------------------------------------------------------------------------------- /pydashie/widgets/text/text.html: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

6 | 7 |

-------------------------------------------------------------------------------- /pydashie/widgets/text/text.scss: -------------------------------------------------------------------------------- 1 | // ---------------------------------------------------------------------------- 2 | // Sass declarations 3 | // ---------------------------------------------------------------------------- 4 | $background-color: #ec663c; 5 | 6 | $title-color: rgba(255, 255, 255, 0.7); 7 | $moreinfo-color: rgba(255, 255, 255, 0.7); 8 | 9 | // ---------------------------------------------------------------------------- 10 | // Widget-text styles 11 | // ---------------------------------------------------------------------------- 12 | .widget-text { 13 | 14 | background-color: $background-color; 15 | 16 | .title { 17 | color: $title-color; 18 | } 19 | 20 | .more-info { 21 | color: $moreinfo-color; 22 | } 23 | 24 | .updated-at { 25 | color: rgba(255, 255, 255, 0.7); 26 | } 27 | 28 | 29 | &.large h3 { 30 | font-size: 65px; 31 | } 32 | } -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | flask>=0.9 2 | CoffeeScript 3 | requests 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | try: 4 | from setuptools import setup, find_packages 5 | except ImportError: 6 | from ez_setup import use_setuptools 7 | use_setuptools() 8 | from setuptools import setup, find_packages 9 | 10 | 11 | setup( 12 | name='PyDashie', 13 | version='0.1dev', 14 | packages=['pydashie',], 15 | install_requires=[ 16 | 'Flask >= 0.10', 17 | 'Coffeescript >= 1.0.8', 18 | 'pyScss >= 1.2.0' 19 | ], 20 | entry_points={ 21 | 'console_scripts': ['pydashie = pydashie.main:run_sample_app'] 22 | }, 23 | license='MIT', 24 | long_description=open('README.rst').read(), 25 | ) 26 | --------------------------------------------------------------------------------