Adam Gomaa avatar Adam Gomaa committed 7f66237

initial code import

Comments (0)

Files changed (16)

Empty file added.

propaneweb/manage.py

+#!/home/akg/var/python/environments/propane/bin/python
+from django.core.management import execute_manager
+try:
+    import settings # Assumed to be in the same directory.
+except ImportError, e:
+    import sys
+    print e
+    sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+    sys.exit(1)
+
+if __name__ == "__main__":
+    execute_manager(settings)

propaneweb/note.py

+"Module for Note object, server-side equivalent of the Backbone Note model"
+
+
+class Note(object):
+    "A text note in my notes directory"
+    directory = "/home/akg/var/notes"
+
+    def __init__(self, fname):
+        self.fname = fname
+
+    def model_data(self):
+        "The data for this Note that should be supplied to the Backbone model"
+        return {
+            "timestamp": self.timestamp(),
+            "text": self.text(),
+            }
+
+    def timestamp(self):
+        from os.path import splitext
+        return splitext(self.fname)[0]
+
+    def text(self):
+        from django.utils.html import urlize
+        # urlize to make http:// clickable
+        return urlize(self.fd().read())
+
+    def fd(self):
+        from os.path import join
+        return open(join(self.directory, self.fname))

propaneweb/settings.py

+# Django settings for propaneweb project.
+from local_settings import *
+import logging
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+    # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+    'default': {
+        'ENGINE': 'django.db.backends.postgresql_psycopg2', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+        'NAME': 'propaneweb',                      # Or path to database file if using sqlite3.
+        'USER': 'akg',                      # Not used with sqlite3.
+        'PASSWORD': '',                  # Not used with sqlite3.
+        'HOST': '',                      # Set to empty string for localhost. Not used with sqlite3.
+        'PORT': '',                      # Set to empty string for default. Not used with sqlite3.
+    }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/New_York'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute filesystem path to the directory that will hold user-uploaded files.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = ''
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = ''
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/django-admin-media/'
+
+# imported from local_settings
+# SECRET_KEY = ''
+
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+    'django.template.loaders.filesystem.Loader',
+    'django.template.loaders.app_directories.Loader',
+#     'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+    'django.middleware.common.CommonMiddleware',
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    #'django.middleware.csrf.CsrfViewMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'django.contrib.messages.middleware.MessageMiddleware',
+)
+
+ROOT_URLCONF = 'propaneweb.urls'
+
+TEMPLATE_DIRS = (
+    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+    # Always use forward slashes, even on Windows.
+    # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+    'django.contrib.auth',
+    'django.contrib.contenttypes',
+    'django.contrib.sessions',
+    'django.contrib.sites',
+    'django.contrib.messages',
+    'django.contrib.admin',
+    'django.contrib.admindocs',
+    'focuslog',
+)
+
+SESSION_COOKIE_NAME = "propaneweb_session"
+
+logging.basicConfig(level=logging.INFO)
+USE_X_FORWARDED_HOST = True

propaneweb/shortcuts.py

+import json
+
+from django.core.urlresolvers import reverse
+
+from django.forms import Media
+
+from django.http import HttpResponse
+from django.http import HttpResponseBadRequest
+from django.http import HttpResponseForbidden
+from django.http import HttpResponseGone
+from django.http import HttpResponseNotAllowed
+from django.http import HttpResponseNotFound
+from django.http import HttpResponseNotModified
+from django.http import HttpResponsePermanentRedirect
+from django.http import HttpResponseRedirect
+from django.http import HttpResponseServerError
+
+from django.contrib.auth.decorators import login_required
+
+from propaneweb import util
+
+render = util.render
+
+

propaneweb/templates/base.html

+<!DOCTYPE html>
+<html>
+<head>
+  <title>{% block head_title %}propaneweb{% endblock %}</title>
+  {% block head_css %}{% endblock %}
+<style type="text/css">{% block inline_css %}
+html, body, div, p, table, thead, tbody, td, tr{margin:0; padding:0;border:0;}
+table{border-spacing:0;}
+{% endblock %}</style>
+  {% block head_js %}{% endblock %}
+
+</head>
+<body>
+{% block body %}{% endblock %}
+{% block end_js %}{% endblock %}
+<script type="text/javascript">
+{% block inline_js %}{% endblock %}
+</script>
+</body>
+</html>

propaneweb/templates/index.html

+{% extends "base.html" %}
+
+
+{% block body %}
+Welcome to propaneweb, {{ request.user }}!
+{% endblock body %}

propaneweb/templates/login.html

+{% extends "base.html" %}
+
+
+{% block body %}
+<form action="{{ request.get_full_path() }}" method="post" enctype="multipart/form-data">
+{% if badlogin %}
+<p>Sorry, your login failed.</p>
+<p>seen username: {{ request.POST.username }}</p>
+<p>seen password: {{ pw }}</p>
+{% endif %}
+<input type="text" name="username"><br>
+<input type="password" name="password"><br>
+<input type="submit" value="login">
+</form>
+{% endblock body %}

propaneweb/templates/meta.html

+{% extends "base.html" %}
+
+
+{% block body %}
+<table style="font-family: monospace;">
+<thead>
+<tr>
+  <th>Var</th>
+  <th>Value</th>
+</tr>
+</thead>
+<tbody>
+  {% macro row(k, v) %}<tr><td>{{ k }}</td><td>{{ v }}</td></tr>{% endmacro %}
+{% macro querydict(name, d) %}{% for k in sorted(d) %}{% for each in d.getlist(k) %}{{ row("{0}[{1}]".format(name, k), each) }}{% endfor %}{% endfor %}{% endmacro %}
+{% for k, v in rows %}{{ row(k, v) }}{% endfor %}
+{{ querydict("GET", request.GET) }}
+{{ querydict("POST", request.POST) }}
+{% for k, v in sorted(request.META.iteritems()) %}{{ row("META[{0}]".format(k), v) }}{% endfor %}
+</tbody>
+</table>
+
+{% endblock body %}

propaneweb/templates/notes-init.js

+$(function(){
+//    window.app.collection.reset({{ notes_data|safe }});
+})

propaneweb/templates/notes.css

+.notes-container
+{
+    margin: auto;
+    width: 95%;
+}
+
+.note
+{
+    border: solid #eee 1px;
+    margin-bottom: 20px;
+    padding: 10px;
+    background-color: #eee
+}
+
+.note h3
+{
+    font-family: DejaVu Sans Mono;
+    margin: 0 auto 20px;
+    text-align: center;
+    font-weight: normal;
+    font-size: 25px;
+}
+
+.note-text
+{
+
+    white-space: pre-wrap;
+    font-family: DejaVu Sans Mono;
+}
+
+.notes-pagination
+{
+    margin: 20px 0px;
+    font-family: DejaVu Sans Mono;
+    text-align: center;
+    font-size: 20px;
+}

propaneweb/templates/notes.html

+{% extends "base.html" %}
+
+{% block head_js %}{{ super() }}
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.2/underscore-min.js"></script>
+<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js"></script>
+<script type="text/javascript" src="?js"></script>
+{% endblock %}
+
+{% block head_css %}{{ super() }}
+<link rel="stylesheet" type="text/css" href="?css">
+{% endblock %}
+
+{% block body %}
+<div id="note-template" style="display: none;">
+  <div class="note" data-note-timestamp="<%- timestamp %>" >
+    <h3><%- timestamp %></h3>
+    <div class="note-text"><%= text %></div>
+  </div>
+</div>
+<div id="notes-view">
+  <div class="notes-pagination"></div>
+  <div class="notes-container"></div>
+  <div class="notes-pagination"></div>
+</div>
+<div id="pagination-template" style="display:none;">
+<% if(pages > 1) { %>
+  <% if(prev) { %>
+    <a href="#" class="prev">previous</a>
+  <% } else { %>
+    <span>previous</span>
+  <% } %>
+
+  <%= range[0] %>..<%= range[1] %> of <%= total %>
+
+  <% if(next) { %>
+    <a href="#" class="next">next</a>
+  <% } else { %>
+    <span>next</span>
+  <% } %>
+<% } %>
+</div>
+
+{% endblock body %}
+

propaneweb/templates/notes.js

+$(function(){
+
+    var get_template = function(selector){
+        return _.template($(selector).html().replace(/&lt;/g, "<").replace(/&gt;/g, ">"));
+    }
+
+// https://gist.github.com/838460
+var PaginatedCollection = Backbone.Collection.extend({
+  initialize: function() {
+    _.bindAll(this, 'parse', 'url', 'pageInfo', 'nextPage', 'previousPage');
+    typeof(options) != 'undefined' || (options = {});
+    this.page = 1;
+    typeof(this.perPage) != 'undefined' || (this.perPage = 10);
+  },
+  fetch: function(options) {
+    typeof(options) != 'undefined' || (options = {});
+    this.trigger("fetching");
+    var self = this;
+    var success = options.success;
+    options.success = function(resp) {
+      self.trigger("fetched");
+      if(success) { success(self, resp); }
+    };
+    return Backbone.Collection.prototype.fetch.call(this, options);
+  },
+  parse: function(resp) {
+    this.page = resp.page;
+    this.perPage = resp.perPage;
+    this.total = resp.total;
+    return resp.models;
+  },
+  url: function() {
+      return this.baseUrl + '?' + $.param({col: "", page: this.page, perPage: this.perPage});
+  },
+  pageInfo: function() {
+    var info = {
+      total: this.total,
+      page: this.page,
+      perPage: this.perPage,
+      pages: Math.ceil(this.total / this.perPage),
+      prev: false,
+      next: false
+    };
+
+    var max = Math.min(this.total, this.page * this.perPage);
+
+    if (this.total == this.pages * this.perPage) {
+      max = this.total;
+    }
+
+    info.range = [(this.page - 1) * this.perPage + 1, max];
+
+    if (this.page > 1) {
+      info.prev = this.page - 1;
+    }
+
+    if (this.page < info.pages) {
+      info.next = this.page + 1;
+    }
+
+    return info;
+  },
+  nextPage: function() {
+    if (!this.pageInfo().next) {
+      return false;
+    }
+    this.page = this.page + 1;
+    return this.fetch();
+  },
+  previousPage: function() {
+    if (!this.pageInfo().prev) {
+      return false;
+    }
+    this.page = this.page - 1;
+    return this.fetch();
+  }
+
+});
+
+
+var Note = Backbone.Model.extend({
+    timestamp_display: function(){
+        var ts = this.get("timestamp");
+        return ts.substr(0, 10);
+    }
+});
+
+var NoteCol = PaginatedCollection.extend({model: Note, baseUrl: '/notes/'});
+
+PaginatedView = Backbone.View.extend({
+  initialize: function() {
+    _.bindAll(this, 'previous', 'next', 'render');
+      this.collection.bind('reset', this.render, this);
+  },
+  events: {
+    'click a.prev': 'previous',
+    'click a.next': 'next'
+  },
+  render: function() {
+      this.el.find(".notes-pagination").html(get_template("#pagination-template")(this.collection.pageInfo()));
+  },
+
+  previous: function() {
+    this.collection.previousPage();
+    return false;
+  },
+
+  next: function() {
+    this.collection.nextPage();
+    return false;
+  }
+});
+
+window.NoteView = Backbone.View.extend({
+    tagName: "div",
+    template: get_template("#note-template"),
+    render: function(){
+        $(this.el).html(this.template(this.template_context()));
+        return this;
+    },
+    template_context: function(){
+        return this.model.attributes;
+    }
+});
+
+var AppView = PaginatedView.extend({
+    el: $("#notes-view"),
+    initialize: function(options){
+        this.collection = new NoteCol();
+        this.constructor.__super__.initialize.apply(this, [options])
+        //this.notes.bind("add", this.addOne, this)
+        this.collection.bind("reset", this.addAll, this)
+        this.collection.fetch();
+    },
+    addOne: function(note){
+        var view = new NoteView({model: note});
+        this.$(".notes-container").append(view.render().el);
+    },
+    addAll: function(){
+        this.$(".notes-container").empty();
+        this.collection.each(this.addOne)
+    }
+})
+
+    window.app = new AppView();
+});

propaneweb/urls.py

+from django.conf.urls.defaults import *
+
+from django.conf import settings
+
+
+from local_settings import music
+
+# Uncomment the next two lines to enable the admin:
+from django.contrib import admin
+admin.autodiscover()
+
+urlpatterns = patterns(
+    'propaneweb.views',
+    url(r'^accounts/login/', 'login'),
+    (r'^accounts/', include('django.contrib.auth.urls')),
+    url(r'^$', 'index', {}),
+    url(r'^music/(.*)', music, {}),
+    url(r'^do/bbping', 'do_bbping', {}),
+    url(r'^meta', 'meta', {}),
+    url(r'^notes/', 'notes', {}),
+)
+

propaneweb/util.py

+import re
+
+def package_path(path=None):
+    import propaneweb
+    import os.path
+    if path:
+        return os.path.join(package_path(), path)
+    return os.path.dirname(propaneweb.__file__)
+
+
+_jinja2_env = None
+def get_jinja2_env():
+    global _jinja2_env
+    if _jinja2_env is None:
+        _jinja2_env = _get_jinja2_env()
+    return _jinja2_env
+
+
+def _get_jinja2_env():
+    import jinja2
+    import os.path
+    import propaneweb.shortcuts
+    env = jinja2.Environment(
+        loader=jinja2.FileSystemLoader(package_path("templates")),
+        autoescape=True,
+        extensions=["jinja2.ext.autoescape", "jinja2.ext.with_",
+                    "jinja2.ext.i18n", "jinja2.ext.do",
+                    "jinja2.ext.loopcontrols",],)
+    env.globals["ps"] = propaneweb.shortcuts
+    import __builtin__
+    env.globals.update(__builtin__.__dict__)
+    return env
+
+def render(*args, **kwargs):
+    import propaneweb.shortcuts as ps
+    assert args or kwargs
+    assert not (args and kwargs)
+    if args:
+        if len(args) == 3:
+            return render(request=args[0], template_name=args[1], **args[2])
+        elif len(args) == 2:
+            return render(request=args[0], template_name=args[1], **kwargs)
+        elif len(args) == 1:
+            context = args[0]
+    else:
+        context = kwargs
+    return ps.HttpResponse(get_jinja2_env().get_template(context["template_name"]).render(context))
+
+
+_human_helper_rx = re.compile('([0-9]+)')
+def human_sort_key(string):
+    return [int(c) if c.isdigit() else c for c in _human_helper_rx.split(string)]
+
+
+def _subprocess_helper(*args, **kwargs):
+    """Call Popen() with *args, **kwargs, and wait in a separate thread
+
+    Allows a web request to return without waiting for the subprocess,
+    without leaving the subprocess in a zombie state.
+
+    returns (process, thread)
+    """
+    import subprocess
+    import threading
+    p = subprocess.Popen(*args, **kwargs)
+    t = threading.Thread(target=p.wait)
+    t.start()
+    return (p, t)
+

propaneweb/views.py

+import propaneweb.shortcuts as ps
+
+@ps.login_required
+def index(request):
+    return ps.render(request, "index.html")
+
+
+def login(request):
+    "Handle login form & POST"
+    from django.contrib.auth import authenticate, login as auth_login
+    if request.user.is_authenticated():
+        return ps.HttpResponseRedirect(request.GET.get("next", "/"))
+    if request.method == "POST":
+        user = authenticate(
+            username=request.POST["username"],
+            password=request.POST["password"])
+        if user is not None:
+            auth_login(request, user)
+            return ps.HttpResponseRedirect(request.GET.get("next", "/"))
+        else:
+            return ps.render(request, "login.html", {"badlogin": True, 'pw': repr(request.POST["password"])})
+
+    return ps.render(request, "login.html")
+
+
+def do_bbping(request):
+    "Callback for BitBucket's POST service to push CS code."
+    if request.method == "POST":
+        # script that does the heavy lifting, can't have this process
+        # hang while that happens
+        ps.util._subprocess_helper(["ag-bb-ping.sh"])
+        return ps.HttpResponseRedirect(request.get_full_path())
+    return ps.HttpResponse("go away, this isn't for humans")
+
+
+def meta(request):
+    "Show some meta info for debugging"
+    rows = []
+    rows.append(("path", request.get_full_path()))
+    rows.append(("path_info", request.path_info))
+    rows.append(("method", request.method))
+    rows.append(("GET", request.GET))
+    rows.append(("POST", request.POST))
+    rows.append(("dir", dir(request)))
+    rows.append(("secure", request.is_secure()))
+    return ps.render(request, "meta.html", {"rows": rows})
+
+
+
+
+def notes(request):
+    "Render notes template"
+    from .note import Note
+
+    if not request.user.is_authenticated():
+        return ps.HttpResponseRedirect("/")
+
+    # I'm the only user anyway
+    if request.user.username != 'akg':
+        return ps.HttpResponseRedirect('/')
+
+    def js_source():
+        "Get static JS"
+        from .util import get_jinja2_env
+        env = get_jinja2_env()
+        source, fname, uptodate = env.loader.get_source(None, "notes.js")
+        return ps.HttpResponse(source, content_type="text/javascript")
+
+    def css_source():
+        "Get static CSS"
+        from .util import get_jinja2_env
+        env = get_jinja2_env()
+        source, fname, uptodate = env.loader.get_source(None, "notes.css")
+        return ps.HttpResponse(source, content_type="text/css")
+
+    def notes_data():
+        "Get JSON data for backbone models"
+        from os import listdir
+        from os.path import join, splitext
+        directory = Note.directory
+
+        notes = []
+        for fname in sorted(listdir(directory), reverse=True):
+            if fname.startswith("."): continue
+            notes.append(Note(fname))
+
+        page = int(request.GET.get("page"))
+        per_page = int(request.GET.get("perPage"))
+        slice_start = per_page * (page - 1)
+        slice_end = slice_start + per_page
+        # data that'll be serialized & passed to Collection.reset()
+        resp_data = {
+            "page": page, "perPage": per_page, "total": len(notes),
+            "models": [n.model_data() for n in notes[slice_start:slice_end]]}
+        return ps.HttpResponse(ps.json.dumps(resp_data), content_type="text/javascript")
+
+
+    if "js" in request.GET:
+        return js_source()
+    if "css" in request.GET:
+        return css_source()
+    if "col" in request.GET:
+        return notes_data()
+
+    return ps.render(request, "notes.html", {})
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.