diff --git a/ox/django/__init__.py b/ox/django/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/ox/django/api/__init__.py b/ox/django/api/__init__.py deleted file mode 100644 index cf410f8..0000000 --- a/ox/django/api/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from actions import actions diff --git a/ox/django/api/actions.py b/ox/django/api/actions.py deleted file mode 100644 index 8579080..0000000 --- a/ox/django/api/actions.py +++ /dev/null @@ -1,136 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from __future__ import division, with_statement -import inspect -import sys - -from django.conf import settings - -from ..shortcuts import render_to_json_response, json_response -from ...utils import json - -def autodiscover(): - # Register api actions from all installed apps - from importlib import import_module - from django.utils.module_loading import module_has_submodule - for app in settings.INSTALLED_APPS: - if app != 'api': - mod = import_module(app) - try: - import_module('%s.views'%app) - except: - if module_has_submodule(mod, 'views'): - raise - -def trim(docstring): - if not docstring: - return '' - # Convert tabs to spaces (following the normal Python rules) - # and split into a list of lines: - lines = docstring.expandtabs().splitlines() - # Determine minimum indentation (first line doesn't count): - indent = sys.maxint - for line in lines[1:]: - stripped = line.lstrip() - if stripped: - indent = min(indent, len(line) - len(stripped)) - # Remove indentation (first line is special): - trimmed = [lines[0].strip()] - if indent < sys.maxint: - for line in lines[1:]: - trimmed.append(line[indent:].rstrip()) - # Strip off trailing and leading blank lines: - while trimmed and not trimmed[-1]: - trimmed.pop() - while trimmed and not trimmed[0]: - trimmed.pop(0) - # Return a single string: - return '\n'.join(trimmed) - - -class ApiActions(dict): - properties = {} - versions = {} - def __init__(self): - - def api(request, data): - ''' - Returns a list of all api actions - takes { - code: boolean, // if true, return source code (optional) - docs: boolean // if true, return doc strings (optional) - } - returns { - actions: { - name: { - cache: boolean, // if false, don't cache results - code: string, // source code - doc: string // doc strings - }, - ... // more actions - } - } - ''' - docs = data.get('docs', False) - code = data.get('code', False) - version = getattr(request, 'version', None) - if version: - _actions = self.versions.get(version, {}).keys() - _actions = list(set(_actions + self.keys())) - else: - _actions = self.keys() - _actions.sort() - actions = {} - for a in _actions: - actions[a] = self.properties[a] - if docs: - actions[a]['doc'] = self.doc(a, version) - if code: - actions[a]['code'] = self.code(a, version) - response = json_response({'actions': actions}) - return render_to_json_response(response) - self.register(api) - - def doc(self, name, version=None): - if version: - f = self.versions[version].get(name, self.get(name)) - else: - f = self[name] - return trim(f.__doc__) - - def code(self, name, version=None): - if version: - f = self.versions[version].get(name, self.get(name)) - else: - f = self[name] - if name != 'api' and hasattr(f, 'func_closure') and f.func_closure: - fc = filter(lambda c: hasattr(c.cell_contents, '__call__'), f.func_closure) - f = fc[len(fc)-1].cell_contents - info = f.func_code.co_filename[len(settings.PROJECT_ROOT)+1:] - info = u'%s:%s' % (info, f.func_code.co_firstlineno) - return info, trim(inspect.getsource(f)) - - def register(self, method, action=None, cache=True, version=None): - if not action: - action = method.func_name - if version: - if not version in self.versions: - self.versions[version] = {} - self.versions[version][action] = method - else: - self[action] = method - self.properties[action] = {'cache': cache} - - def unregister(self, action): - if action in self: - del self[action] - -actions = ApiActions() - -def error(request, data): - ''' - This action is used to test API error codes. It should return a 503 error. - ''' - success = error_is_success - return render_to_json_response({}) -actions.register(error) diff --git a/ox/django/api/urls.py b/ox/django/api/urls.py deleted file mode 100644 index 5346a98..0000000 --- a/ox/django/api/urls.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - -from django.conf.urls import url - -import views - -import actions -actions.autodiscover() - -urlpatterns = [ - url(r'^$', views.api), -] diff --git a/ox/django/api/views.py b/ox/django/api/views.py deleted file mode 100644 index 43591ed..0000000 --- a/ox/django/api/views.py +++ /dev/null @@ -1,54 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from __future__ import division, with_statement - -import json - -from django.shortcuts import render_to_response -from django.template import RequestContext -from django.conf import settings - -from ..shortcuts import render_to_json_response, json_response - -from actions import actions - -def api(request): - if request.META['REQUEST_METHOD'] == "OPTIONS": - response = render_to_json_response({'status': {'code': 200, - 'text': 'use POST'}}) - response['Access-Control-Allow-Origin'] = '*' - return response - if request.META['REQUEST_METHOD'] != "POST" or ( - not 'action' in request.POST and request.META.get('CONTENT_TYPE') != 'application/json' - ): - methods = actions.keys() - api = [] - for f in sorted(methods): - api.append({'name': f, - 'doc': actions.doc(f).replace('\n', '
\n')}) - context = RequestContext(request, { - 'api': api, - 'settings': settings, - 'sitename': settings.SITENAME - }) - return render_to_response('api.html', context) - if request.META.get('CONTENT_TYPE') == 'application/json': - r = json.loads(request.body) - action = r['action'] - data = r.get('data', {}) - else: - action = request.POST['action'] - data = json.loads(request.POST.get('data', '{}')) - version = getattr(request, 'version', None) - if version: - f = actions.versions.get(version, {}).get(action, actions.get(action)) - else: - f = actions.get(action) - if f: - response = f(request, data) - else: - response = render_to_json_response(json_response(status=400, - text='Unknown action %s' % action)) - response['Access-Control-Allow-Origin'] = '*' - return response - diff --git a/ox/django/decorators.py b/ox/django/decorators.py deleted file mode 100644 index 98aa9dd..0000000 --- a/ox/django/decorators.py +++ /dev/null @@ -1,32 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - -try: - from django.contrib.auth.decorators import wraps -except: - from django.utils.functional import wraps -from shortcuts import render_to_json_response - -def login_required_json(function=None): - """ - Decorator for views that checks that the user is logged in - return json error if not logged in. - """ - - def _wrapped_view(request, *args, **kwargs): - if request.user.is_authenticated(): - return function(request, *args, **kwargs) - return render_to_json_response({'status': {'code': 401, 'text': 'login required'}}) - return wraps(function)(_wrapped_view) - -def admin_required_json(function=None): - """ - Decorator for views that checks that the user is logged in - return json error if not logged in. - """ - - def _wrapped_view(request, *args, **kwargs): - if request.user.is_authenticated() and request.user.profile.get_level() == 'admin': - return function(request, *args, **kwargs) - return render_to_json_response({'status': {'code': 403, 'text': 'permission denied'}}) - return wraps(function)(_wrapped_view) diff --git a/ox/django/fields.py b/ox/django/fields.py deleted file mode 100644 index 9270468..0000000 --- a/ox/django/fields.py +++ /dev/null @@ -1,99 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -import time -import datetime -import copy - -from django.db import models -from django.utils import datetime_safe -from six import string_types - -from ox.utils import json - - -def to_json(python_object): - if isinstance(python_object, datetime.datetime): - if python_object.year < 1900: - tt = python_object.timetuple() - value = '%d-%02d-%02dT%02d:%02d%02dZ' % tuple(list(tt)[:6]) - else: - value = python_object.strftime('%Y-%m-%dT%H:%M:%SZ') - return {'__class__': 'datetime.datetime', - '__value__': value} - if isinstance(python_object, datetime_safe.datetime): - return {'__class__': 'datetime.datetime', - '__value__': python_object.strftime('%Y-%m-%dT%H:%M:%SZ')} - if isinstance(python_object, time.struct_time): - return {'__class__': 'time.asctime', - '__value__': time.asctime(python_object)} - try: - if isinstance(python_object, bytes): - return {'__class__': 'bytes', - '__value__': list(python_object)} - except: - pass - raise TypeError(repr(python_object) + ' is not JSON serializable') - -def from_json(json_object): - if '__class__' in json_object: - if json_object['__class__'] == 'bytes': - return bytes(json_object['__value__']) - if json_object['__class__'] == 'datetime_safe.datetime' \ - or json_object['__class__'] == 'datetime.datetime': - return datetime_safe.datetime.strptime(json_object['__value__'], '%Y-%m-%dT%H:%M:%SZ') - if json_object['__class__'] == 'time.asctime': - return time.strptime(json_object['__value__']) - return json_object - -class DictField(models.TextField): - _type = dict - - def loads(self, value): - return json.loads(value, object_hook=from_json) - - def dumps(self, obj): - return json.dumps(obj, default=to_json, ensure_ascii=False) - - def from_db_value(self, value, expression, connection, context): - if value is None: - return value - if isinstance(value, self._type): - return value - try: - value = self.loads(value) - except: - raise Exception('failed to parse value: %s' % value) - if value is not None: - assert isinstance(value, self._type) - return value - - def get_prep_value(self, value): - if isinstance(value, self._type): - value = self.dumps(value) - if value is not None: - assert isinstance(value, string_types) - value = models.TextField.get_prep_value(self, value) - return value - - def get_default(self): - if self.has_default(): - if callable(self.default): - return self.default() - return copy.deepcopy(self.default) - return super(DictField, self).get_default() - -class TupleField(DictField): - _type = (tuple, list) - - def loads(self, value): - value = DictField.loads(self, value) - if isinstance(value, list): - value = tuple(value) - return value - -try: - from south.modelsinspector import add_introspection_rules - add_introspection_rules([], ["^ox.django\.fields\.DictField"]) - add_introspection_rules([], ["^ox.django\.fields\.TupleField"]) -except: - pass diff --git a/ox/django/http.py b/ox/django/http.py deleted file mode 100644 index c07c36f..0000000 --- a/ox/django/http.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -import os -import mimetypes -from datetime import datetime, timedelta -from six.moves.urllib.parse import quote - -from django.http import HttpResponse, Http404 -from django.conf import settings - - -def HttpFileResponse(path, content_type=None, filename=None): - if not os.path.exists(path): - raise Http404 - if not content_type: - content_type = mimetypes.guess_type(path)[0] - if not content_type: - content_type = 'application/octet-stream' - - if getattr(settings, 'XACCELREDIRECT', False): - response = HttpResponse() - response['Content-Length'] = os.stat(path).st_size - - for PREFIX in ('STATIC', 'MEDIA'): - root = getattr(settings, PREFIX+'_ROOT', '') - url = getattr(settings, PREFIX+'_URL', '') - if root and path.startswith(root): - path = url + path[len(root)+1:] - if not isinstance(path, bytes): - path = path.encode('utf-8') - response['X-Accel-Redirect'] = path - if content_type: - response['Content-Type'] = content_type - elif getattr(settings, 'XSENDFILE', False): - response = HttpResponse() - if not isinstance(path, bytes): - path = path.encode('utf-8') - response['X-Sendfile'] = path - if content_type: - response['Content-Type'] = content_type - response['Content-Length'] = os.stat(path).st_size - else: - response = HttpResponse(open(path), content_type=content_type) - if filename: - if not isinstance(filename, bytes): - filename = filename.encode('utf-8') - response['Content-Disposition'] = "attachment; filename*=UTF=8''%s" % quote(filename) - - response['Expires'] = datetime.strftime(datetime.utcnow() + timedelta(days=1), "%a, %d-%b-%Y %H:%M:%S GMT") - - def allow_access(): - for key in ('X-Accel-Redirect', 'X-Sendfile'): - if key in response: - del response[key] - response['Access-Control-Allow-Origin'] = '*' - response.allow_access = allow_access - return response - diff --git a/ox/django/middleware.py b/ox/django/middleware.py deleted file mode 100644 index 751c24a..0000000 --- a/ox/django/middleware.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - -from .shortcuts import HttpErrorJson, render_to_json_response - -class ExceptionMiddleware(object): - def process_exception(self, request, exception): - if isinstance(exception, HttpErrorJson): - return render_to_json_response(exception.response) - return None - -class ChromeFrameMiddleware(object): - def process_response(self, request, response): - response['X-UA-Compatible'] = 'chrome=1' - return response diff --git a/ox/django/monitor.py b/ox/django/monitor.py deleted file mode 100644 index f87e839..0000000 --- a/ox/django/monitor.py +++ /dev/null @@ -1,114 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from __future__ import print_function - -import os -import sys -import signal -import threading -import atexit -from six.moves.queue import Queue - -_interval = 1.0 -_times = {} -_files = [] - -_running = False -_queue = Queue() -_lock = threading.Lock() - -def _restart(path): - _queue.put(True) - prefix = 'monitor (pid=%d):' % os.getpid() - print('%s Change detected to \'%s\'.' % (prefix, path), file=sys.stderr) - print('%s Triggering process restart.' % prefix, file=sys.stderr) - os.kill(os.getpid(), signal.SIGINT) - -def _modified(path): - try: - # If path doesn't denote a file and were previously - # tracking it, then it has been removed or the file type - # has changed so force a restart. If not previously - # tracking the file then we can ignore it as probably - # pseudo reference such as when file extracted from a - # collection of modules contained in a zip file. - - if not os.path.isfile(path): - return path in _times - - # Check for when file last modified. - - mtime = os.stat(path).st_mtime - if path not in _times: - _times[path] = mtime - - # Force restart when modification time has changed, even - # if time now older, as that could indicate older file - # has been restored. - - if mtime != _times[path]: - return True - except: - # If any exception occured, likely that file has been - # been removed just before stat(), so force a restart. - - return True - - return False - -def _monitor(): - while 1: - # Check modification times on all files in sys.modules. - - for module in list(sys.modules.values()): - if not hasattr(module, '__file__'): - continue - path = getattr(module, '__file__') - if not path: - continue - if os.path.splitext(path)[1] in ['.pyc', '.pyo', '.pyd']: - path = path[:-1] - if _modified(path): - return _restart(path) - - # Check modification times on files which have - # specifically been registered for monitoring. - - for path in _files: - if _modified(path): - return _restart(path) - - # Go to sleep for specified interval. - - try: - return _queue.get(timeout=_interval) - except: - pass - -_thread = threading.Thread(target=_monitor) -_thread.setDaemon(True) - -def _exiting(): - try: - _queue.put(True) - except: - pass - _thread.join() - -atexit.register(_exiting) - -def track(path): - if not path in _files: - _files.append(path) - -def start(interval=1.0): - global _interval - if interval < _interval: - _interval = interval - - global _running - _lock.acquire() - if not _running: - _running = True - _thread.start() - _lock.release() diff --git a/ox/django/query.py b/ox/django/query.py deleted file mode 100644 index 38ab9a6..0000000 --- a/ox/django/query.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 - -from django.db.models.sql import Query -from django.db.models.sql.compiler import SQLCompiler -from django.db import connections -import django.db.models.query - -''' -models.py: ------------------------------------ -from ox.django.query import QuerySet - -class Manager(models.Manager): - def get_query_set(self): - return QuerySet(self.model) - -class Model(models.Model): - ... - objects = Manager() -''' - -class NullLastSQLCompiler(SQLCompiler): - - def get_order_by(self): - result = super(NullLastSQLCompiler, self).get_order_by() - if self.query.nulls_last and result \ - and self.connection.vendor == 'postgresql': - return [(expr, (sql + ' NULLS LAST', params, is_ref)) - for (expr, (sql, params, is_ref)) in result] - return result - -class NullsLastQuery(Query): - nulls_last = False - - def clone(self, *args, **kwargs): - obj = super(NullsLastQuery, self).clone(*args, **kwargs) - obj.nulls_last = self.nulls_last - return obj - - def get_compiler(self, using=None, connection=None): - if using is None and connection is None: - raise ValueError("Need either using or connection") - if using: - connection = connections[using] - return NullLastSQLCompiler(self, connection, using) - -class QuerySet(django.db.models.query.QuerySet): - - def __init__(self, model=None, query=None, using=None, **kwargs): - super(QuerySet, self).__init__(model=model, query=query, using=None, **kwargs) - self.query = query or NullsLastQuery(self.model) - - def order_by(self, *args, **kwargs): - nulls_last = kwargs.pop('nulls_last', False) - obj = super(QuerySet, self).order_by(*args, **kwargs) - obj.query.nulls_last = nulls_last - return obj diff --git a/ox/django/shortcuts.py b/ox/django/shortcuts.py deleted file mode 100644 index 2e6a4fb..0000000 --- a/ox/django/shortcuts.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from __future__ import print_function -import datetime -from django.utils import datetime_safe -from django.http import HttpResponse, Http404 -try: - import simplejson as json -except ImportError: - from django.utils import simplejson as json -from django.conf import settings - -class HttpErrorJson(Http404): - def __init__(self, response): - self.response = response - -def json_response(data=None, status=200, text='ok'): - if not data: - data = {} - return {'status': {'code': status, 'text': text}, 'data': data} - -def _to_json(python_object): - if isinstance(python_object, datetime.datetime): - if python_object.year < 1900: - tt = python_object.timetuple() - return '%d-%02d-%02dT%02d:%02d%02dZ' % tuple(list(tt)[:6]) - return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') - if isinstance(python_object, datetime_safe.datetime): - return python_object.strftime('%Y-%m-%dT%H:%M:%SZ') - raise TypeError(u'%s %s is not JSON serializable' % (repr(python_object), type(python_object))) - -def render_to_json_response(dictionary, content_type="text/json", status=200): - indent=None - if settings.DEBUG: - content_type = "text/javascript" - indent = 2 - if getattr(settings, 'JSON_DEBUG', False): - print(json.dumps(dictionary, indent=2, default=_to_json, ensure_ascii=False).encode('utf-8')) - - return HttpResponse(json.dumps(dictionary, indent=indent, default=_to_json, - ensure_ascii=False).encode('utf-8'), content_type=content_type, status=status) - -def get_object_or_404_json(klass, *args, **kwargs): - from django.shortcuts import _get_queryset - queryset = _get_queryset(klass) - try: - return queryset.get(*args, **kwargs) - except queryset.model.DoesNotExist: - response = {'status': {'code': 404, - 'text': '%s not found' % queryset.model._meta.object_name}} - raise HttpErrorJson(response) - diff --git a/ox/django/utils.py b/ox/django/utils.py deleted file mode 100644 index cbc0067..0000000 --- a/ox/django/utils.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from django.utils.datetime_safe import datetime -from django.http import HttpResponse,Http404 -from django.core.servers.basehttp import FileWrapper -from django.conf import settings - -import mimetypes -import os - -def basic_sendfile(fname,download_name=None): - if not os.path.exists(fname): - raise Http404 - - wrapper = FileWrapper(open(fname,"r")) - - content_type = mimetypes.guess_type(fname)[0] - response = HttpResponse(wrapper, content_type=content_type) - response['Content-Length'] = os.path.getsize(fname) - - if download_name: - response['Content-Disposition'] = "attachment; filename=%s"%download_name - - return response - -def x_sendfile(fname,download_name=None): - if not os.path.exists(fname): - raise Http404 - - content_type = mimetypes.guess_type(fname)[0] - response = HttpResponse('', content_type=content_type) - response['Content-Length'] = os.path.getsize(fname) - response['X-Sendfile'] = fname - - if download_name: - response['Content-Disposition'] = "attachment; filename=%s"%download_name - - return response - -try: - __sendfile = getattr(settings,'SENDFILE',False) == 'x_sendfile' -except: - __sendfile = False -if __sendfile == 'x_sendfile': - sendfile = x_sendfile -else: - sendfile = basic_sendfile - diff --git a/ox/django/views.py b/ox/django/views.py deleted file mode 100644 index 24a20d6..0000000 --- a/ox/django/views.py +++ /dev/null @@ -1,67 +0,0 @@ -# -*- coding: utf-8 -*- -# vi:si:et:sw=4:sts=4:ts=4 -from six import StringIO, PY2 -from six.moves import urllib -from six.moves import http_cookiejar as cookielib - -from celery.utils import get_full_cls_name -from celery.backends import default_backend - -from django.http import HttpResponse -from django.conf import settings - -from shortcuts import json_response -import ox - - -def task_status(request, task_id): - response = json_response(status=200, text='ok') - status = default_backend.get_status(task_id) - res = default_backend.get_result(task_id) - response['data'] = { - 'id': task_id, - 'status': status, - 'result': res - } - if status in default_backend.EXCEPTION_STATES: - traceback = default_backend.get_traceback(task_id) - response['data'].update({'result': str(res.args[0]), - 'exc': get_full_cls_name(res.__class__), - 'traceback': traceback}) - return response - -class SessionCookieJar(cookielib.LWPCookieJar): - def save(self): - return "#LWP-Cookies-2.0\n" + self.as_lwp_str() - - def load(self, data, ignore_discard=True, ignore_expires=True): - f = StringIO(data) - self._really_load(f, 'memory', ignore_discard, ignore_expires) - -def api_proxy(request): - ''' - settings.OXAPI_URL =... - from ox.django.views import api_proxy - urlpatterns = patterns('', - url(r'^api/$', api_proxy) - ''' - url = settings.OXAPI_URL - cj = SessionCookieJar() - if 'cj' in request.session: - cj.load(request.session['cj']) - opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) - opener.addheaders = [ - ('User-Agent', request.META.get('HTTP_USER_AGENT')) - ] - form = ox.MultiPartForm() - for key in request.POST: - form.add_field(key, request.POST[key]) - r = urllib.request.Request(url) - body = form.body() - r.add_header('Content-type', form.get_content_type()) - r.add_header('Content-length', len(body)) - r.add_data(body) - f = opener.open(r) - response = HttpResponse(f.read()) - request.session['cj'] = cj.save() - return response diff --git a/ox/django/widgets.py b/ox/django/widgets.py deleted file mode 100644 index b22e57e..0000000 --- a/ox/django/widgets.py +++ /dev/null @@ -1,9 +0,0 @@ -import django.newforms as forms -from string import Template -from django.utils.safestring import mark_safe - -class FirefoggWidget(forms.FileInput): - def render(self, name, value, attrs=None): - tpl = Template(u"""

This should be a Firefogg widget for $name, current value: $value

""") - return mark_safe(tpl.substitute(name=name, value=value)) - diff --git a/setup.py b/setup.py index 249a887..a8cf816 100644 --- a/setup.py +++ b/setup.py @@ -49,7 +49,7 @@ setup( url="https://wiki.0x2620.org/wiki/python-ox", download_url="https://code.0x2620.org/python-ox/download", license="GPLv3", - packages=['ox', 'ox.django', 'ox.django.api', 'ox.torrent', 'ox.web'], + packages=['ox', 'ox.torrent', 'ox.web'], install_requires=['six>=1.5.2', 'chardet', 'feedparser'], keywords = [ ],