<commit_before>from django.contrib.admin.filters import SimpleListFilter
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.template.response import TemplateResponse
from django.contrib import admin

from taggit.models import TaggedItem

from .forms import TagObjectForm, get_fk_form_class

class AdminAssignActionBase():
    action_label = _('Choose object to assign')

    def _assign_action_handler(self, fieldname, actionname, request, queryset, label=None):

        opts = self.model._meta
        # Check that the user has change permission for the actual model
        if not self.has_change_permission(request):
            raise PermissionDenied

        Form = self._get_assign_action_form_class(fieldname)
        # User has already chosen the other req
        if request.POST.get('obj'):
            form = Form(request.POST)
            if form.is_valid():
                assign_obj = form.cleaned_data['obj']
                for obj in queryset:
                    self._execute_assign_action(obj, fieldname, assign_obj)
                self.message_user(request, _("Successfully assigned."))
                return None
        else:
            form = Form()

        context = {
            'opts': opts,
            'queryset': queryset,
            'media': self.media,
            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
            'form': form,
            'headline': label or self.action_label,
            'actionname': actionname,
            'applabel': opts.app_label
        }

        # Display the confirmation page
        return TemplateResponse(request, 'helper/admin/assign_all.html',
            context)

    def _get_assign_action_form_class(self, fieldname):
        return get_fk_form_class(self.model, fieldname, self.admin_site)

    def _execute_assign_action(self, obj, fieldname, assign_obj):
        setattr(obj, fieldname, assign_obj)
        obj.save()

def make_admin_assign_action(fieldname, label=None):
    action_name = 'assign_%s' % fieldname

    def _assign(self, request, queryset):
        return self._assign_action_handler(fieldname, action_name,
                                          request, queryset, label=label)

    _assign.short_description = label or _("Add %s to all selected") % fieldname

    class AdminAssignAction(AdminAssignActionBase):
        actions = [action_name]

    setattr(AdminAssignAction, action_name, _assign)

    return AdminAssignAction

class AdminTagAllMixIn(object):
    def tag_all(self, request, queryset):
        """
        Add tag to all selected objects

        """
        opts = self.model._meta
        # Check that the user has change permission for the actual model
        if not self.has_change_permission(request):
            raise PermissionDenied

        field, autocomplete_url = self.tag_all_config

        # User has already chosen the other req
        if request.POST.get('tags'):
            form = TagObjectForm(request.POST, tags=[],
                                 autocomplete_url=autocomplete_url)
            if form.is_valid():
                tags = form.cleaned_data['tags']
                for obj in queryset:
                    getattr(obj, field).add(*tags)
                    obj.save()
                self.message_user(request, _("Successfully added %s") % field)
                # Return None to display the change list page again.
                return None
            self.message_user(request, _("Form invalid"))

        tags = set()
        form = TagObjectForm(tags=tags,
                             autocomplete_url=autocomplete_url)

        context = {
            'opts': opts,
            'queryset': queryset,
            'media': self.media,
            'form': form,
            'headline': _('Set these tags for all selected items:'),
            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
            'applabel': opts.app_label
        }

        # Display the confirmation page
        return TemplateResponse(request, 'admin_utils/admin_tag_all.html',
            context)
    tag_all.short_description = _("Add tag to all selected")

class NullFilter(SimpleListFilter):
    """
    Taken from
    http://stackoverflow.com/questions/7691890/filtering-django-admin-by-null-is-not-null
    under CC-By 3.0
    """
    title = ''

    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('1', _('Has value')),
            ('0', _('None')),
        )

    def queryset(self, request, queryset):
        kwargs = {
            '%s' % self.parameter_name: None,
        }
        if self.value() == '0':
            return queryset.filter(**kwargs)
        if self.value() == '1':
            return queryset.exclude(**kwargs)
        return queryset

def make_nullfilter(field, title):
    return type(str('%sNullFilter' % field.title()), (NullFilter,), {
        'title': title,
        'parameter_name': field
    })

class GreaterZeroFilter(SimpleListFilter):
    title = ''
    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('1', _('Greater zero')),
            ('0', _('Zero')),
        )

    def queryset(self, request, queryset):
        if self.value() == '0':
            return queryset.filter(**{
                '%s' % self.parameter_name: 0
            })
        if self.value() == '1':
            return queryset.filter(**{
                '%s__gt' % self.parameter_name: 0
            })
        return queryset

def make_greaterzerofilter(field, title):
    return type(str('%sGreaterZeroFilter' % field.title()), (GreaterZeroFilter,), {
        'title': title,
        'parameter_name': field
    })

class EmptyFilter(SimpleListFilter):
    title = ''
    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('0', _('Is empty')),
            ('1', _('Is not empty')),

        )

    def queryset(self, request, queryset):
        kwargs = {
            '%s' % self.parameter_name: '',
        }
        if self.value() == '0':
            return queryset.filter(**kwargs)
        if self.value() == '1':
            return queryset.exclude(**kwargs)
        return queryset

def make_emptyfilter(field, title):
    return type(str('%sEmptyFilter' % field.title()), (EmptyFilter,), {
        'title': title,
        'parameter_name': field
    })

class TaggitListFilter(SimpleListFilter):
    """
    A custom filter class that can be used to filter by taggit tags in the admin.
    """

    # Human-readable title which will be displayed in the
    # right admin sidebar just above the filter options.
    title = _('tags')

    # Parameter for the filter that will be used in the URL query.
    parameter_name = 'tag'
    tag_class = TaggedItem

    def lookups(self, request, model_admin):
        """
        Returns a list of tuples. The first element in each tuple is the coded value
        for the option that will appear in the URL query. The second element is the
        human-readable name for the option that will appear in the right sidebar.
        """
        filters = []
        tags = self.tag_class.tags_for(model_admin.model)
        for tag in tags:
            filters.append((tag.slug, _(tag.name)))
        return filters

    def queryset(self, request, queryset):
        """
        Returns the filtered queryset based on the value provided in the query
        string and retrievable via `self.value()`.
        """
        if self.value():
            return queryset.filter(tags__slug__in=[self.value()])

class ForeignKeyFilter(admin.FieldListFilter):
    template = "helper/admin/fk_filter.html"

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(
            field, request, params, model, model_admin, field_path)
        self.lookup_val = request.GET.get(self.field_path, None)
        self.create_used_parameters()

    def create_used_parameters(self):
        param = self.field_path
        val = self.used_parameters.pop(param, None)
        if val is not None:
            if val == 'isnull':
                self.used_parameters['{}__isnull'.format(param)] = True
            elif val == 'notnull':
                self.used_parameters['{}__isnull'.format(param)] = False
            else:
                self.used_parameters['{}__in'.format(param)] = val.split(',')

    def expected_parameters(self):
        return [self.field_path]

    def choices(self, cl):
        return [{
            'value': self.lookup_val,
            'field_path': self.field_path,
            'query_string': cl.get_query_string({},
                [self.field_path]),
        }]

class SearchFilter(ForeignKeyFilter):
    def create_used_parameters(self):
        param = self.field_path
        val = self.used_parameters.pop(param, None)
        if val is not None:
            self.used_parameters['{}__startswith'.format(param)] = val

class MultiFilterMixin:
    template = 'helper/admin/multi_filter.html'
    lookup_name = ''

    def queryset(self, request, queryset):
        if request.GET.get(self.parameter_name):
            lookup = self.parameter_name + self.lookup_name
            values = self.value_as_list()
            includes = [v for v in values if not v.startswith('~')]
            excludes = [v[1:] for v in values if v.startswith('~')]
            if includes:
                for inc in includes:
                    queryset = queryset.filter(**{lookup: [inc]})
            if excludes:
                queryset = queryset.exclude(**{lookup: excludes})
        return queryset

    def value_as_list(self):
        return self.value().split(',') if self.value() else []

    def choices(self, changelist):

        def amend_query_string(include=None, clear=None, exclude=None):
            selections = self.value_as_list()
            if include and include not in selections:
                selections.append(include)
                exclude_val = '~{}'.format(include)
                if exclude_val in selections:
                    selections.remove(exclude_val)
            if exclude:
                exclude_val = '~{}'.format(exclude)
                if exclude in selections:
                    selections.remove(exclude)
                if exclude_val not in selections:
                    selections.append(exclude_val)
            if clear:
                if clear in selections:
                    selections.remove(clear)
                exclude_val = '~{}'.format(clear)
                if exclude_val in selections:
                    selections.remove(exclude_val)
            if selections:
                csv = ','.join(selections)
                return changelist.get_query_string({self.parameter_name: csv})
            else:
                return changelist.get_query_string(remove=[self.parameter_name])

        yield {
            'selected': self.value() is None,
            'query_string': changelist.get_query_string(remove=[self.parameter_name]),
            'display': _('All'),
            'reset': True,
        }
        values = self.value_as_list()
        for lookup, title in self.lookup_choices:
            yield {
                'included': str(lookup) in values,
                'excluded': '~{}'.format(lookup) in values,
                'query_string': changelist.get_query_string({self.parameter_name: lookup}),
                'include_query_string': amend_query_string(include=str(lookup)),
                'clear_query_string': amend_query_string(clear=str(lookup)),
                'exclude_query_string': amend_query_string(exclude=str(lookup)),
                'display': title,
            }
<commit_msg>Refactor admin assign action utils<commit_after>from django.contrib.admin.filters import SimpleListFilter
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import PermissionDenied
from django.template.response import TemplateResponse
from django.contrib import admin

from taggit.models import TaggedItem

from .forms import TagObjectForm, get_fk_form_class

class AdminAssignActionBase():
    action_label = _('Choose object to assign')

    def _assign_action_handler(self, fieldname, actionname, request, queryset, label=None):

        opts = self.model._meta
        # Check that the user has change permission for the actual model
        if not self.has_change_permission(request):
            raise PermissionDenied

        Form = self._get_assign_action_form_class(fieldname)
        # User has already chosen the other req
        if request.POST.get('obj'):
            form = Form(request.POST)
            if form.is_valid():
                assign_obj = form.cleaned_data['obj']
                self._execute_assign_action_qs(queryset, fieldname, assign_obj)
                self.message_user(request, _("Successfully assigned."))
                return None
        else:
            form = Form()

        context = {
            'opts': opts,
            'queryset': queryset,
            'media': self.media,
            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
            'form': form,
            'headline': label or self.action_label,
            'actionname': actionname,
            'applabel': opts.app_label
        }

        # Display the confirmation page
        return TemplateResponse(request, 'helper/admin/assign_all.html',
            context)

    def _get_assign_action_form_class(self, fieldname):
        return get_fk_form_class(self.model, fieldname, self.admin_site)

    def _execute_assign_action_qs(self, queryset, fieldname, assign_obj):
        for obj in queryset:
            self._execute_assign_action(obj, fieldname, assign_obj)

    def _execute_assign_action(self, obj, fieldname, assign_obj):
        setattr(obj, fieldname, assign_obj)
        obj.save()

def make_admin_assign_action(fieldname, label=None):
    action_name = 'assign_%s' % fieldname

    def _assign(self, request, queryset):
        return self._assign_action_handler(fieldname, action_name,
                                          request, queryset, label=label)

    _assign.short_description = label or _("Add %s to all selected") % fieldname

    class AdminAssignAction(AdminAssignActionBase):
        actions = [action_name]

    setattr(AdminAssignAction, action_name, _assign)

    return AdminAssignAction

class AdminTagAllMixIn(object):
    def tag_all(self, request, queryset):
        """
        Add tag to all selected objects

        """
        opts = self.model._meta
        # Check that the user has change permission for the actual model
        if not self.has_change_permission(request):
            raise PermissionDenied

        field, autocomplete_url = self.tag_all_config

        # User has already chosen the other req
        if request.POST.get('tags'):
            form = TagObjectForm(request.POST, tags=[],
                                 autocomplete_url=autocomplete_url)
            if form.is_valid():
                tags = form.cleaned_data['tags']
                for obj in queryset:
                    getattr(obj, field).add(*tags)
                    obj.save()
                self.message_user(request, _("Successfully added %s") % field)
                # Return None to display the change list page again.
                return None
            self.message_user(request, _("Form invalid"))

        tags = set()
        form = TagObjectForm(tags=tags,
                             autocomplete_url=autocomplete_url)

        context = {
            'opts': opts,
            'queryset': queryset,
            'media': self.media,
            'form': form,
            'headline': _('Set these tags for all selected items:'),
            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
            'applabel': opts.app_label
        }

        # Display the confirmation page
        return TemplateResponse(request, 'admin_utils/admin_tag_all.html',
            context)
    tag_all.short_description = _("Add tag to all selected")

class NullFilter(SimpleListFilter):
    """
    Taken from
    http://stackoverflow.com/questions/7691890/filtering-django-admin-by-null-is-not-null
    under CC-By 3.0
    """
    title = ''

    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('1', _('Has value')),
            ('0', _('None')),
        )

    def queryset(self, request, queryset):
        kwargs = {
            '%s' % self.parameter_name: None,
        }
        if self.value() == '0':
            return queryset.filter(**kwargs)
        if self.value() == '1':
            return queryset.exclude(**kwargs)
        return queryset

def make_nullfilter(field, title):
    return type(str('%sNullFilter' % field.title()), (NullFilter,), {
        'title': title,
        'parameter_name': field
    })

class GreaterZeroFilter(SimpleListFilter):
    title = ''
    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('1', _('Greater zero')),
            ('0', _('Zero')),
        )

    def queryset(self, request, queryset):
        if self.value() == '0':
            return queryset.filter(**{
                '%s' % self.parameter_name: 0
            })
        if self.value() == '1':
            return queryset.filter(**{
                '%s__gt' % self.parameter_name: 0
            })
        return queryset

def make_greaterzerofilter(field, title):
    return type(str('%sGreaterZeroFilter' % field.title()), (GreaterZeroFilter,), {
        'title': title,
        'parameter_name': field
    })

class EmptyFilter(SimpleListFilter):
    title = ''
    parameter_name = ''

    def lookups(self, request, model_admin):
        return (
            ('0', _('Is empty')),
            ('1', _('Is not empty')),

        )

    def queryset(self, request, queryset):
        kwargs = {
            '%s' % self.parameter_name: '',
        }
        if self.value() == '0':
            return queryset.filter(**kwargs)
        if self.value() == '1':
            return queryset.exclude(**kwargs)
        return queryset

def make_emptyfilter(field, title):
    return type(str('%sEmptyFilter' % field.title()), (EmptyFilter,), {
        'title': title,
        'parameter_name': field
    })

class TaggitListFilter(SimpleListFilter):
    """
    A custom filter class that can be used to filter by taggit tags in the admin.
    """

    # Human-readable title which will be displayed in the
    # right admin sidebar just above the filter options.
    title = _('tags')

    # Parameter for the filter that will be used in the URL query.
    parameter_name = 'tag'
    tag_class = TaggedItem

    def lookups(self, request, model_admin):
        """
        Returns a list of tuples. The first element in each tuple is the coded value
        for the option that will appear in the URL query. The second element is the
        human-readable name for the option that will appear in the right sidebar.
        """
        filters = []
        tags = self.tag_class.tags_for(model_admin.model)
        for tag in tags:
            filters.append((tag.slug, _(tag.name)))
        return filters

    def queryset(self, request, queryset):
        """
        Returns the filtered queryset based on the value provided in the query
        string and retrievable via `self.value()`.
        """
        if self.value():
            return queryset.filter(tags__slug__in=[self.value()])

class ForeignKeyFilter(admin.FieldListFilter):
    template = "helper/admin/fk_filter.html"

    def __init__(self, field, request, params, model, model_admin, field_path):
        super().__init__(
            field, request, params, model, model_admin, field_path)
        self.lookup_val = request.GET.get(self.field_path, None)
        self.create_used_parameters()

    def create_used_parameters(self):
        param = self.field_path
        val = self.used_parameters.pop(param, None)
        if val is not None:
            if val == 'isnull':
                self.used_parameters['{}__isnull'.format(param)] = True
            elif val == 'notnull':
                self.used_parameters['{}__isnull'.format(param)] = False
            else:
                self.used_parameters['{}__in'.format(param)] = val.split(',')

    def expected_parameters(self):
        return [self.field_path]

    def choices(self, cl):
        return [{
            'value': self.lookup_val,
            'field_path': self.field_path,
            'query_string': cl.get_query_string({},
                [self.field_path]),
        }]

class SearchFilter(ForeignKeyFilter):
    def create_used_parameters(self):
        param = self.field_path
        val = self.used_parameters.pop(param, None)
        if val is not None:
            self.used_parameters['{}__startswith'.format(param)] = val

class MultiFilterMixin:
    template = 'helper/admin/multi_filter.html'
    lookup_name = ''

    def queryset(self, request, queryset):
        if request.GET.get(self.parameter_name):
            lookup = self.parameter_name + self.lookup_name
            values = self.value_as_list()
            includes = [v for v in values if not v.startswith('~')]
            excludes = [v[1:] for v in values if v.startswith('~')]
            if includes:
                for inc in includes:
                    queryset = queryset.filter(**{lookup: [inc]})
            if excludes:
                queryset = queryset.exclude(**{lookup: excludes})
        return queryset

    def value_as_list(self):
        return self.value().split(',') if self.value() else []

    def choices(self, changelist):

        def amend_query_string(include=None, clear=None, exclude=None):
            selections = self.value_as_list()
            if include and include not in selections:
                selections.append(include)
                exclude_val = '~{}'.format(include)
                if exclude_val in selections:
                    selections.remove(exclude_val)
            if exclude:
                exclude_val = '~{}'.format(exclude)
                if exclude in selections:
                    selections.remove(exclude)
                if exclude_val not in selections:
                    selections.append(exclude_val)
            if clear:
                if clear in selections:
                    selections.remove(clear)
                exclude_val = '~{}'.format(clear)
                if exclude_val in selections:
                    selections.remove(exclude_val)
            if selections:
                csv = ','.join(selections)
                return changelist.get_query_string({self.parameter_name: csv})
            else:
                return changelist.get_query_string(remove=[self.parameter_name])

        yield {
            'selected': self.value() is None,
            'query_string': changelist.get_query_string(remove=[self.parameter_name]),
            'display': _('All'),
            'reset': True,
        }
        values = self.value_as_list()
        for lookup, title in self.lookup_choices:
            yield {
                'included': str(lookup) in values,
                'excluded': '~{}'.format(lookup) in values,
                'query_string': changelist.get_query_string({self.parameter_name: lookup}),
                'include_query_string': amend_query_string(include=str(lookup)),
                'clear_query_string': amend_query_string(clear=str(lookup)),
                'exclude_query_string': amend_query_string(exclude=str(lookup)),
                'display': title,
            }
<|endoftext|>


<commit_before>
from django.db import connection, transaction, models
from django.db.backends.util import truncate_name
from django.dispatch import dispatcher
from django.conf import settings

class DatabaseOperations(object):

    """
    Generic SQL implementation of the DatabaseOperations.
    Some of this code comes from Django Evolution.
    """

    def __init__(self):
        self.debug = False
        self.deferred_sql = []

    def execute(self, sql, params=[]):
        """
        Executes the given SQL statement, with optional parameters.
        If the instance's debug attribute is True, prints out what it executes.
        """
        cursor = connection.cursor()
        if self.debug:
            print "   = %s" % sql, params
        cursor.execute(sql, params)
        try:
            return cursor.fetchall()
        except:
            return []

    def add_deferred_sql(self, sql):
        """
        Add a SQL statement to the deferred list, that won't be executed until
        this instance's execute_deferred_sql method is run.
        """
        self.deferred_sql.append(sql)

    def execute_deferred_sql(self):
        """
        Executes all deferred SQL, resetting the deferred_sql list
        """
        for sql in self.deferred_sql:
            self.execute(sql)

        self.deferred_sql = []

    def create_table(self, table_name, fields):
        """
        Creates the table 'table_name'. 'fields' is a tuple of fields,
        each repsented by a 2-part tuple of field name and a
        django.db.models.fields.Field object
        """
        qn = connection.ops.quote_name
        columns = [
            self.column_sql(table_name, field_name, field)
            for field_name, field in fields
        ]

        self.execute('CREATE TABLE %s (%s);' % (table_name, ', '.join([col for col in columns])))

    add_table = create_table # Alias for consistency's sake

    def rename_table(self, old_table_name, table_name):
        """
        Renames the table 'old_table_name' to 'table_name'.
        """
        if old_table_name == table_name:
            # No Operation
            return
        qn = connection.ops.quote_name
        params = (qn(old_table_name), qn(table_name))
        self.execute('ALTER TABLE %s RENAME TO %s;' % params)

    def delete_table(self, table_name):
        """
        Deletes the table 'table_name'.
        """
        qn = connection.ops.quote_name
        params = (qn(table_name), )
        self.execute('DROP TABLE %s;' % params)

    drop_table = delete_table

    def add_column(self, table_name, name, field):
        """
        Adds the column 'name' to the table 'table_name'.
        Uses the 'field' paramater, a django.db.models.fields.Field instance,
        to generate the necessary sql

        @param table_name: The name of the table to add the column to
        @param name: The name of the column to add
        @param field: The field to use
        """
        qn = connection.ops.quote_name
        sql = self.column_sql(table_name, name, field)
        params = (
            qn(table_name),
            sql,
        )
        sql = 'ALTER TABLE %s ADD COLUMN %s;' % params
        self.execute(sql)

    def column_sql(self, table_name, field_name, field, tablespace=''):
        """
        Creates the SQL snippet for a column. Used by add_column and add_table.
        """
        qn = connection.ops.quote_name
        field.set_attributes_from_name(field_name)

        # hook for the field to do any resolution prior to it's attributes being queried
        if hasattr(field, 'south_init'):
            field.south_init()

        sql = field.db_type()
        if not sql:
            return None

        field_output = [qn(field.column), sql]
        field_output.append('%sNULL' % (not field.null and 'NOT ' or ''))
        if field.primary_key:
            field_output.append('PRIMARY KEY')
        elif field.unique:
            field_output.append('UNIQUE')

        tablespace = field.db_tablespace or tablespace
        if tablespace and connection.features.supports_tablespaces and field.unique:
            # We must specify the index tablespace inline, because we
            # won't be generating a CREATE INDEX statement for this field.
            field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))

        sql = ' '.join(field_output)
        sqlparams = ()
        # if the field is "NOT NULL" and a default value is provided, create the column with it
        # this allows the addition of a NOT NULL field to a table with existing rows
        if not field.null and field.has_default():
            default = field.get_default()
            if isinstance(default, basestring):
                default = "'%s'" % default
            sql += " DEFAULT %s"
            sqlparams = (default)

        if field.rel:
            self.add_deferred_sql(
                self.foreign_key_sql(
                    table_name,
                    field.column,
                    field.rel.to._meta.db_table,
                    field.rel.to._meta.get_field(field.rel.field_name).column
                )
            )

        if field.db_index:
            self.add_deferred_sql(self.create_index_sql(table_name, [field.column]))

        return sql % sqlparams

    def foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name):
        """
        Generates a full SQL statement to add a foreign key constraint
        """
        constraint_name = '%s_refs_%s_%x' % (from_column_name, to_column_name, abs(hash((from_table_name, to_table_name))))
        return 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (
            from_table_name,
            truncate_name(constraint_name, connection.ops.max_name_length()),
            from_column_name,
            to_table_name,
            to_column_name,
            connection.ops.deferrable_sql() # Django knows this
        )

    def create_index_name(self, table_name, column_names):
        """
        Generate a unique name for the index
        """
        index_unique_name = ''
        if len(column_names) > 1:
            index_unique_name = '_%x' % abs(hash((table_name, ','.join(column_names))))

        return '%s_%s%s' % (table_name, column_names[0], index_unique_name)

    def create_index_sql(self, table_name, column_names, unique=False, db_tablespace=''):
        """
        Generates a create index statement on 'table_name' for a list of 'column_names'
        """
        if not column_names:
            print "No column names supplied on which to create an index"
            return ''

        if db_tablespace and connection.features.supports_tablespaces:
            tablespace_sql = ' ' + connection.ops.tablespace_sql(db_tablespace)
        else:
            tablespace_sql = ''

        index_name = self.create_index_name(table_name, column_names)
        qn = connection.ops.quote_name
        return 'CREATE %sINDEX %s ON %s (%s)%s;' % (
            unique and 'UNIQUE ' or '',
            index_name,
            table_name,
            ','.join([qn(field) for field in column_names]),
            tablespace_sql
            )

    def create_index(self, table_name, column_names, unique=False, db_tablespace=''):
        """ Executes a create index statement """
        sql = self.create_index_sql(table_name, column_names, unique, db_tablespace)
        self.execute(sql)

    def delete_column(self, table_name, name):
        """
        Deletes the column 'column_name' from the table 'table_name'.
        """
        qn = connection.ops.quote_name
        params = (qn(table_name), qn(name))
        self.execute('ALTER TABLE %s DROP COLUMN %s CASCADE;' % params, [])

    def rename_column(self, table_name, old, new):
        """
        Renames the column 'old' from the table 'table_name' to 'new'.
        """
        raise NotImplementedError("rename_column has no generic SQL syntax")

    def start_transaction(self):
        """
        Makes sure the following commands are inside a transaction.
        Must be followed by a (commit|rollback)_transaction call.
        """
        transaction.commit_unless_managed()
        transaction.enter_transaction_management()
        transaction.managed(True)

    def commit_transaction(self):
        """
        Commits the current transaction.
        Must be preceded by a start_transaction call.
        """
        transaction.commit()
        transaction.leave_transaction_management()

    def rollback_transaction(self):
        """
        Rolls back the current transaction.
        Must be preceded by a start_transaction call.
        """
        transaction.rollback()
        transaction.leave_transaction_management()

    def send_create_signal(self, app_label, model_names):
        """
        Sends a post_syncdb signal for the model specified.

        If the model is not found (perhaps it's been deleted?),
        no signal is sent.

        TODO: The behavior of django.contrib.* apps seems flawed in that
        they don't respect created_models.  Rather, they blindly execute
        over all models within the app sending the signal.  This is a
        patch we should push Django to make  For now, this should work.
        """
        app = models.get_app(app_label)
        if not app:
            return

        created_models = []
        for model_name in model_names:
            model = models.get_model(app_label, model_name)
            if model:
                created_models.append(model)

        if created_models:
            # syncdb defaults -- perhaps take these as options?
            verbosity = 1
            interactive = True
            dispatcher.send(signal=models.signals.post_syncdb, sender=app,
                app=app, created_models=created_models,
                verbosity=verbosity, interactive=interactive)

    def mock_model(self, model_name, db_table, db_tablespace='',
                    pk_field_name='id', pk_field_type=models.AutoField,
                    pk_field_kwargs={}):
        """
        Generates a MockModel class that provides enough information
        to be used by a foreign key/many-to-many relationship.

        Migrations should prefer to use these rather than actual models
        as models could get deleted over time, but these can remain in
        migration files forever.
        """
        class MockOptions(object):
            def __init__(self):
                self.db_table = db_table
                self.db_tablespace = db_tablespace or settings.DEFAULT_TABLESPACE
                self.object_name = model_name
                self.module_name = model_name.lower()

                if pk_field_type == models.AutoField:
                    pk_field_kwargs['primary_key'] = True

                self.pk = pk_field_type(**pk_field_kwargs)
                self.pk.set_attributes_from_name(pk_field_name)

            def get_field_by_name(self, field_name):
                # we only care about the pk field
                return (self.pk, self.model, True, False)

            def get_field(self, name):
                # we only care about the pk field
                return self.pk

        class MockModel(object):
            _meta = None

        # We need to return an actual class object here, not an instance
        MockModel._meta = MockOptions()
        MockModel._meta.model = MockModel
        return MockModel
<commit_msg>don't create an index for any column that's also unique.  the create table statement should create a unique index by default for this column<commit_after>
from django.db import connection, transaction, models
from django.db.backends.util import truncate_name
from django.dispatch import dispatcher
from django.conf import settings

class DatabaseOperations(object):

    """
    Generic SQL implementation of the DatabaseOperations.
    Some of this code comes from Django Evolution.
    """

    def __init__(self):
        self.debug = False
        self.deferred_sql = []

    def execute(self, sql, params=[]):
        """
        Executes the given SQL statement, with optional parameters.
        If the instance's debug attribute is True, prints out what it executes.
        """
        cursor = connection.cursor()
        if self.debug:
            print "   = %s" % sql, params
        cursor.execute(sql, params)
        try:
            return cursor.fetchall()
        except:
            return []

    def add_deferred_sql(self, sql):
        """
        Add a SQL statement to the deferred list, that won't be executed until
        this instance's execute_deferred_sql method is run.
        """
        self.deferred_sql.append(sql)

    def execute_deferred_sql(self):
        """
        Executes all deferred SQL, resetting the deferred_sql list
        """
        for sql in self.deferred_sql:
            self.execute(sql)

        self.deferred_sql = []

    def create_table(self, table_name, fields):
        """
        Creates the table 'table_name'. 'fields' is a tuple of fields,
        each repsented by a 2-part tuple of field name and a
        django.db.models.fields.Field object
        """
        qn = connection.ops.quote_name
        columns = [
            self.column_sql(table_name, field_name, field)
            for field_name, field in fields
        ]

        self.execute('CREATE TABLE %s (%s);' % (table_name, ', '.join([col for col in columns])))

    add_table = create_table # Alias for consistency's sake

    def rename_table(self, old_table_name, table_name):
        """
        Renames the table 'old_table_name' to 'table_name'.
        """
        if old_table_name == table_name:
            # No Operation
            return
        qn = connection.ops.quote_name
        params = (qn(old_table_name), qn(table_name))
        self.execute('ALTER TABLE %s RENAME TO %s;' % params)

    def delete_table(self, table_name):
        """
        Deletes the table 'table_name'.
        """
        qn = connection.ops.quote_name
        params = (qn(table_name), )
        self.execute('DROP TABLE %s;' % params)

    drop_table = delete_table

    def add_column(self, table_name, name, field):
        """
        Adds the column 'name' to the table 'table_name'.
        Uses the 'field' paramater, a django.db.models.fields.Field instance,
        to generate the necessary sql

        @param table_name: The name of the table to add the column to
        @param name: The name of the column to add
        @param field: The field to use
        """
        qn = connection.ops.quote_name
        sql = self.column_sql(table_name, name, field)
        params = (
            qn(table_name),
            sql,
        )
        sql = 'ALTER TABLE %s ADD COLUMN %s;' % params
        self.execute(sql)

    def column_sql(self, table_name, field_name, field, tablespace=''):
        """
        Creates the SQL snippet for a column. Used by add_column and add_table.
        """
        qn = connection.ops.quote_name
        field.set_attributes_from_name(field_name)

        # hook for the field to do any resolution prior to it's attributes being queried
        if hasattr(field, 'south_init'):
            field.south_init()

        sql = field.db_type()
        if not sql:
            return None

        field_output = [qn(field.column), sql]
        field_output.append('%sNULL' % (not field.null and 'NOT ' or ''))
        if field.primary_key:
            field_output.append('PRIMARY KEY')
        elif field.unique:
            field_output.append('UNIQUE')

        tablespace = field.db_tablespace or tablespace
        if tablespace and connection.features.supports_tablespaces and field.unique:
            # We must specify the index tablespace inline, because we
            # won't be generating a CREATE INDEX statement for this field.
            field_output.append(connection.ops.tablespace_sql(tablespace, inline=True))

        sql = ' '.join(field_output)
        sqlparams = ()
        # if the field is "NOT NULL" and a default value is provided, create the column with it
        # this allows the addition of a NOT NULL field to a table with existing rows
        if not field.null and field.has_default():
            default = field.get_default()
            if isinstance(default, basestring):
                default = "'%s'" % default
            sql += " DEFAULT %s"
            sqlparams = (default)

        if field.rel:
            self.add_deferred_sql(
                self.foreign_key_sql(
                    table_name,
                    field.column,
                    field.rel.to._meta.db_table,
                    field.rel.to._meta.get_field(field.rel.field_name).column
                )
            )

        if field.db_index and not field.unique:
            self.add_deferred_sql(self.create_index_sql(table_name, [field.column]))

        return sql % sqlparams

    def foreign_key_sql(self, from_table_name, from_column_name, to_table_name, to_column_name):
        """
        Generates a full SQL statement to add a foreign key constraint
        """
        constraint_name = '%s_refs_%s_%x' % (from_column_name, to_column_name, abs(hash((from_table_name, to_table_name))))
        return 'ALTER TABLE %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s)%s;' % (
            from_table_name,
            truncate_name(constraint_name, connection.ops.max_name_length()),
            from_column_name,
            to_table_name,
            to_column_name,
            connection.ops.deferrable_sql() # Django knows this
        )

    def create_index_name(self, table_name, column_names):
        """
        Generate a unique name for the index
        """
        index_unique_name = ''
        if len(column_names) > 1:
            index_unique_name = '_%x' % abs(hash((table_name, ','.join(column_names))))

        return '%s_%s%s' % (table_name, column_names[0], index_unique_name)

    def create_index_sql(self, table_name, column_names, unique=False, db_tablespace=''):
        """
        Generates a create index statement on 'table_name' for a list of 'column_names'
        """
        if not column_names:
            print "No column names supplied on which to create an index"
            return ''

        if db_tablespace and connection.features.supports_tablespaces:
            tablespace_sql = ' ' + connection.ops.tablespace_sql(db_tablespace)
        else:
            tablespace_sql = ''

        index_name = self.create_index_name(table_name, column_names)
        qn = connection.ops.quote_name
        return 'CREATE %sINDEX %s ON %s (%s)%s;' % (
            unique and 'UNIQUE ' or '',
            index_name,
            table_name,
            ','.join([qn(field) for field in column_names]),
            tablespace_sql
            )

    def create_index(self, table_name, column_names, unique=False, db_tablespace=''):
        """ Executes a create index statement """
        sql = self.create_index_sql(table_name, column_names, unique, db_tablespace)
        self.execute(sql)

    def delete_column(self, table_name, name):
        """
        Deletes the column 'column_name' from the table 'table_name'.
        """
        qn = connection.ops.quote_name
        params = (qn(table_name), qn(name))
        self.execute('ALTER TABLE %s DROP COLUMN %s CASCADE;' % params, [])

    def rename_column(self, table_name, old, new):
        """
        Renames the column 'old' from the table 'table_name' to 'new'.
        """
        raise NotImplementedError("rename_column has no generic SQL syntax")

    def start_transaction(self):
        """
        Makes sure the following commands are inside a transaction.
        Must be followed by a (commit|rollback)_transaction call.
        """
        transaction.commit_unless_managed()
        transaction.enter_transaction_management()
        transaction.managed(True)

    def commit_transaction(self):
        """
        Commits the current transaction.
        Must be preceded by a start_transaction call.
        """
        transaction.commit()
        transaction.leave_transaction_management()

    def rollback_transaction(self):
        """
        Rolls back the current transaction.
        Must be preceded by a start_transaction call.
        """
        transaction.rollback()
        transaction.leave_transaction_management()

    def send_create_signal(self, app_label, model_names):
        """
        Sends a post_syncdb signal for the model specified.

        If the model is not found (perhaps it's been deleted?),
        no signal is sent.

        TODO: The behavior of django.contrib.* apps seems flawed in that
        they don't respect created_models.  Rather, they blindly execute
        over all models within the app sending the signal.  This is a
        patch we should push Django to make  For now, this should work.
        """
        app = models.get_app(app_label)
        if not app:
            return

        created_models = []
        for model_name in model_names:
            model = models.get_model(app_label, model_name)
            if model:
                created_models.append(model)

        if created_models:
            # syncdb defaults -- perhaps take these as options?
            verbosity = 1
            interactive = True
            dispatcher.send(signal=models.signals.post_syncdb, sender=app,
                app=app, created_models=created_models,
                verbosity=verbosity, interactive=interactive)

    def mock_model(self, model_name, db_table, db_tablespace='',
                    pk_field_name='id', pk_field_type=models.AutoField,
                    pk_field_kwargs={}):
        """
        Generates a MockModel class that provides enough information
        to be used by a foreign key/many-to-many relationship.

        Migrations should prefer to use these rather than actual models
        as models could get deleted over time, but these can remain in
        migration files forever.
        """
        class MockOptions(object):
            def __init__(self):
                self.db_table = db_table
                self.db_tablespace = db_tablespace or settings.DEFAULT_TABLESPACE
                self.object_name = model_name
                self.module_name = model_name.lower()

                if pk_field_type == models.AutoField:
                    pk_field_kwargs['primary_key'] = True

                self.pk = pk_field_type(**pk_field_kwargs)
                self.pk.set_attributes_from_name(pk_field_name)

            def get_field_by_name(self, field_name):
                # we only care about the pk field
                return (self.pk, self.model, True, False)

            def get_field(self, name):
                # we only care about the pk field
                return self.pk

        class MockModel(object):
            _meta = None

        # We need to return an actual class object here, not an instance
        MockModel._meta = MockOptions()
        MockModel._meta.model = MockModel
        return MockModel
<|endoftext|>


<commit_before># -*- coding:utf-8 -*-
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.core.urlresolvers import reverse
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from sorl.thumbnail import ImageField
from unidecode import unidecode

from dcf import settings as dcf_settings

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    phone = models.CharField(_('phone'), max_length=30, null=True, blank=True)
    receive_news = models.BooleanField(_('receive news'), default=True, db_index=True)

    def allow_add_item(self):
        return self.user.item_set.count() < dcf_settings.DCF_ITEM_PER_USER_LIMIT

    @staticmethod
    def get_or_create_for_user(user):
        if hasattr(user, 'profile'):
            return user.profile
        else:
            return Profile.objects.create(user=user)

class Section(models.Model):
    title = models.CharField(_('title'), max_length=100)

    def __unicode__(self):
        return self.title

    def count(self):
        return Item.objects.filter(is_active=True)\
            .filter(group__section=self).count()

    class Meta:
        verbose_name = _('section')
        verbose_name_plural = _('sections')

class Group(models.Model):
    slug = models.SlugField(blank=True, null=True)
    title = models.CharField(_('title'), max_length=100)
    section = models.ForeignKey('Section', verbose_name=_('section'))

    def __unicode__(self):
        return u'%s - %s' % (self.section.title, self.title)

    @cached_property
    def count(self):
        return self.item_set.filter(is_active=True).count()

    class Meta:
        verbose_name = _('group')
        verbose_name_plural = _('groups')
        ordering = ['section__title', 'title', ]

    def save(self, *args, **kwargs):
        if self.slug is None:
            self.slug = slugify(unidecode(self.title))
        super(Group, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('group', kwargs={'pk': self.pk, 'slug': self.slug})

class Item(models.Model):
    slug = models.SlugField(blank=True, null=True, max_length=100)
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    group = models.ForeignKey(Group, verbose_name=_('group'))

    title = models.CharField(_('title'), max_length=100)
    description = models.TextField(_('description'))
    price = models.DecimalField(_('price'), max_digits=10, decimal_places=2)
    phone = models.CharField(_('phone'), max_length=30)
    is_active = models.BooleanField(_('display'), default=True, db_index=True)
    updated = models.DateTimeField(_('updated'), auto_now=True, db_index=True)
    posted = models.DateTimeField(_('posted'), auto_now_add=True)

    def __unicode__(self):
        return self.title

    class Meta:
        verbose_name = _('item')
        verbose_name_plural = _('items')
        ordering = ('-updated', )

    def get_absolute_url(self):
        return reverse('item', kwargs={
            'pk': self.pk,
            'slug': self.slug
        })

    def get_keywords(self):
        # TODO need more optimal keywords selection
        return ",".join(set(self.description.split()))

    @cached_property
    def image_count(self):
        return self.image_set.count()

    @cached_property
    def featured_image(self):
        return self.image_set.all().first()

    @cached_property
    def related_items(self):
        qs = Item.objects \
            .filter(is_active=True) \
            .filter(group=self.group) \
            .exclude(pk=self.pk)

        return qs[:dcf_settings.DCF_RELATED_LIMIT]

    def save(self, *args, **kwargs):
        if self.slug is None:
            self.slug = slugify(unidecode(self.title))
        super(Item, self).save(*args, **kwargs)

class Image(models.Model):
    item = models.ForeignKey(Item)
    file = ImageField(_('image'), upload_to='images')
<commit_msg>Removed unused import<commit_after># -*- coding:utf-8 -*-
from django.conf import settings
from django.core.urlresolvers import reverse
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from sorl.thumbnail import ImageField
from unidecode import unidecode

from dcf import settings as dcf_settings

class Profile(models.Model):
    user = models.OneToOneField(settings.AUTH_USER_MODEL)
    phone = models.CharField(_('phone'), max_length=30, null=True, blank=True)
    receive_news = models.BooleanField(_('receive news'), default=True, db_index=True)

    def allow_add_item(self):
        return self.user.item_set.count() < dcf_settings.DCF_ITEM_PER_USER_LIMIT

    @staticmethod
    def get_or_create_for_user(user):
        if hasattr(user, 'profile'):
            return user.profile
        else:
            return Profile.objects.create(user=user)

class Section(models.Model):
    title = models.CharField(_('title'), max_length=100)

    def __unicode__(self):
        return self.title

    def count(self):
        return Item.objects.filter(is_active=True)\
            .filter(group__section=self).count()

    class Meta:
        verbose_name = _('section')
        verbose_name_plural = _('sections')

class Group(models.Model):
    slug = models.SlugField(blank=True, null=True)
    title = models.CharField(_('title'), max_length=100)
    section = models.ForeignKey('Section', verbose_name=_('section'))

    def __unicode__(self):
        return u'%s - %s' % (self.section.title, self.title)

    @cached_property
    def count(self):
        return self.item_set.filter(is_active=True).count()

    class Meta:
        verbose_name = _('group')
        verbose_name_plural = _('groups')
        ordering = ['section__title', 'title', ]

    def save(self, *args, **kwargs):
        if self.slug is None:
            self.slug = slugify(unidecode(self.title))
        super(Group, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return reverse('group', kwargs={'pk': self.pk, 'slug': self.slug})

class Item(models.Model):
    slug = models.SlugField(blank=True, null=True, max_length=100)
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    group = models.ForeignKey(Group, verbose_name=_('group'))

    title = models.CharField(_('title'), max_length=100)
    description = models.TextField(_('description'))
    price = models.DecimalField(_('price'), max_digits=10, decimal_places=2)
    phone = models.CharField(_('phone'), max_length=30)
    is_active = models.BooleanField(_('display'), default=True, db_index=True)
    updated = models.DateTimeField(_('updated'), auto_now=True, db_index=True)
    posted = models.DateTimeField(_('posted'), auto_now_add=True)

    def __unicode__(self):
        return self.title

    class Meta:
        verbose_name = _('item')
        verbose_name_plural = _('items')
        ordering = ('-updated', )

    def get_absolute_url(self):
        return reverse('item', kwargs={
            'pk': self.pk,
            'slug': self.slug
        })

    def get_keywords(self):
        # TODO need more optimal keywords selection
        return ",".join(set(self.description.split()))

    @cached_property
    def image_count(self):
        return self.image_set.count()

    @cached_property
    def featured_image(self):
        return self.image_set.all().first()

    @cached_property
    def related_items(self):
        qs = Item.objects \
            .filter(is_active=True) \
            .filter(group=self.group) \
            .exclude(pk=self.pk)

        return qs[:dcf_settings.DCF_RELATED_LIMIT]

    def save(self, *args, **kwargs):
        if self.slug is None:
            self.slug = slugify(unidecode(self.title))
        super(Item, self).save(*args, **kwargs)

class Image(models.Model):
    item = models.ForeignKey(Item)
    file = ImageField(_('image'), upload_to='images')
<|endoftext|>


<commit_before>import os, time, io, re, Queue, threading, subprocess, msvcrt
from optparse import OptionParser,OptionGroup
from ast import literal_eval
from difflib import Differ
from PIL import Image,ImageGrab,ImageOps

def Run(box, timestamp, txtQueue, save):
    if save:
        image = Image.open(SaveScreenshotTime(timestamp))
    else:
        image = GetScreenshot()
    boxImage = GetBoxImage(image, box)
    ConvertImage(boxImage, timestamp, txtQueue)
    #AppendCombatLog(combatTime, txtQueue, first, save)

def SaveScreenshot():
    image = ImageGrab.grab()
    image.save('ddo_ss_single.bmp', dpi=(200, 200))

def SaveScreenshotTime(time):
    ss = 'ddo_ss_' + time + '.bmp'
    image = ImageGrab.grab()
    image.save(ss, dpi=(200, 200))
    return ss

def GetScreenshot():
    image = ImageGrab.grab()
    return image

def SaveBox(coords):
    image = Image.open('ddo_ss_single.bmp')
    region = image.crop(coords)
    region.save('ddo_box_ss_single.bmp', dpi=(200, 200))

def GetBoxImage(image, coords):
    region = image.crop(coords)
    region = ConvertToBW(region)
    return region

def ConvertToBW(image):
    image = image.convert("RGB")
    pixels = image.load()
    for y in xrange(image.size[1]):
        for x in xrange(image.size[0]):
            if pixels[x, y][0] < 80:
                pixels[x, y] = (0, 0, 0, 255)
            if pixels[x, y][1] < 126:
                pixels[x, y] = (0, 0, 0, 255)
            if pixels[x, y][2] > 0:
                pixels[x, y] = (255, 255, 255, 255)
    return image

def ConvertImage(image, time, txtQueue):
    image = image.resize((image.width * 6, image.height * 6), Image.ANTIALIAS)
    regionName = 'ddo_bw_' + time + '.bmp'
    image.save(regionName, dpi=(200,200))
    Tesseract(regionName, "ddo_convert_" + time, txtQueue)

def ConvertAndSaveImage(box_filepath, coords, count):
    image = Image.open(box_filepath)
    image = image.crop(coords)
    image = image.resize((image.width * 6, image.height * 6), Image.ANTIALIAS)
    image = ConvertToBW(image)
    name = 'ddo-ddo_font-exp' + count + '.bmp'
    image.save(name, dpi=(200,200))

def Tesseract(input_filename, output_filename, txtQueue):
    # Call the tesseract.exe file with arguments
    args = ['tesseract.exe', input_filename, output_filename, '-l', 'ddo', '-psm', '6']
    # Use the stderr and stdout to ignore tesseract print statements
    proc = subprocess.Popen(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    retcode = proc.wait()
    txtQueue.append(output_filename + ".txt")
    try:
        # Don't want to keep the enlarged box image file since it is pretty big
        os.remove(input_filename)
    except Exception as e:
        print "Couldn't remove temporary files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
        pass

def AppendCombatLog(combatTime, txtQueue, first, save):
    regex = re.compile(r'^\((Combat|Effects)\):.+$', re.IGNORECASE)

    if first[0]:
        if len(txtQueue) > 0:
            with open(txtQueue[0], 'r') as file1:
                initialFile = file1.readlines()

            # Remove junk lines that the OCR failed to parse
            if not regex.match(initialFile[0]):
                initialFile = initialFile[1:]
            # Use list comprehension to remove extra newlines added by OCR
            initialFile = [ x for x in initialFile if not x == '\n' ]

            with open("ddo_combatlog_" + combatTime + ".txt", 'w') as file:
                # Compare the files, get difference deltas
                for line in initialFile:
                    file.write(line)
            first[0] = False
    else:
        if len(txtQueue) >= 2:
            with open(txtQueue[0], 'r') as file1:
                previous = file1.readlines()
            with open(txtQueue[1], 'r') as file2:
                next = file2.readlines()

            # Remove junk lines that the OCR failed to parse
            if not regex.match(previous[0]):
                previous = previous[1:]
            # Use list comprehension to remove extra newlines added by OCR
            previous = [ x for x in previous if not x == '\n' ]

            # Remove junk lines that the OCR failed to parse
            if not regex.match(next[0]):
                next = next[1:]
            # Use list comprehension to remove extra newlines added by OCR
            next = [ x for x in next if not x == '\n' ]

            with open("ddo_combatlog_" + combatTime + ".txt", 'a') as file:
                # Compare the files, get difference deltas
                for line in Differ().compare(previous, next):
                    # Only want to add the new lines from the next file
                    if line[:2] == "+ ":
                        # Don't want the "+ " identifiers
                        file.write(line[2:])
            if not save:
                try:
                    os.remove(txtQueue[0])
                except Exception as e:
                    print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                    pass
            txtQueue.pop(0)

def ProgramOptions():
    parser = OptionParser()
    parser.add_option("-m", "--mode", dest="mode", default="S", help="Mode of operation. Options: S, B, C, F. S - Screenshot mode, B - Box Image mode, C - Conver to BW, F - Full mode. Default: S", type="string", metavar="MODE")
    parser.add_option("-b", "--box", dest="box", default=(0,0,0,0), help="This takes a series of 4 numbers that are the coordinates of the combat log box. (TopLeftCoord,TopRightCoord,BottomRightCoord,BottomLeftCoord)", metavar="COORDS")
    parser.add_option("-i", "--interval", dest="interval", default=2, help="Interval that the program will take screeshots in seconds. Default: 2.", type="int", metavar="TIME")
    parser.add_option("-s", "--savefiles", dest="saveFiles", action="store_true", help="If present, the progam will not delete the images/text it creates.", metavar="FILES")
    parser.add_option("-p", "--picture", dest="pic", help="Image.", metavar="PIC")
    parser.add_option("-c", "--count", dest="count", help="Count.")
    parser.add_option_group(OptionGroup(parser, "Press Escape to exit. Example run:", "python ddo-logger.py -m F -b (0,0,500,800) -i 2 -s"))
    return parser

def main():
    (options, args) = ProgramOptions().parse_args()

    if options.mode == "S":
        SaveScreenshot()
    elif options.mode == "B":
        SaveBox(literal_eval(options.box))
    elif options.mode == "C":
        timestamp = str(int(time.time()))
        ConvertAndSaveImage(options.pic, literal_eval(options.box), options.count)
    elif options.mode == "F":
        first = [True]
        combatTime = str(int(time.time()))
        txtQueue = []
        while True:
            if msvcrt.kbhit():
                if ord(msvcrt.getch()) == 27:
                    if len(txtQueue) > 0:
                        for file in txtQueue:
                            try:
                                os.remove(file)
                            except Exception as e:
                                print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                                pass
                        files = [i for i in os.listdir(os.getcwd()) if os.path.isfile(os.path.join(os.getcwd(),i)) and 'ddo_bw_' in i]
                        for file in files:
                            try:
                                os.remove(file)
                            except Exception as e:
                                print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                                pass
                    break
            AppendCombatLog(combatTime, txtQueue, first, options.saveFiles)
            timestamp = str(int(time.time()))
            thread = threading.Thread(target=Run, args=[literal_eval(options.box), timestamp, txtQueue, options.saveFiles])
            thread.daemon = True
            thread.start()
            #first = False
            time.sleep(options.interval)
    else:
        print("Invalid mode provided.")

if __name__ == '__main__':
    main()
<commit_msg>File and line reading fixes<commit_after>import os, time, io, re, Queue, threading, subprocess, msvcrt
from optparse import OptionParser,OptionGroup
from ast import literal_eval
from difflib import Differ
from PIL import Image,ImageGrab,ImageOps

def Run(box, timestamp, txtQueue, save):
    if save:
        image = Image.open(SaveScreenshotTime(timestamp))
    else:
        image = GetScreenshot()
    boxImage = GetBoxImage(image, box)
    ConvertImage(boxImage, timestamp, txtQueue)
    #AppendCombatLog(combatTime, txtQueue, first, save)

def SaveScreenshot():
    image = ImageGrab.grab()
    image.save('ddo_ss_single.bmp', dpi=(200, 200))

def SaveScreenshotTime(time):
    ss = 'ddo_ss_' + time + '.bmp'
    image = ImageGrab.grab()
    image.save(ss, dpi=(200, 200))
    return ss

def GetScreenshot():
    image = ImageGrab.grab()
    return image

def SaveBox(coords):
    image = Image.open('ddo_ss_single.bmp')
    region = image.crop(coords)
    region.save('ddo_box_ss_single.bmp', dpi=(200, 200))

def GetBoxImage(image, coords):
    region = image.crop(coords)
    region = ConvertToBW(region)
    return region

def ConvertToBW(image):
    image = image.convert("RGB")
    pixels = image.load()
    for y in xrange(image.size[1]):
        for x in xrange(image.size[0]):
            if pixels[x, y][0] < 80:
                pixels[x, y] = (0, 0, 0, 255)
            if pixels[x, y][1] < 126:
                pixels[x, y] = (0, 0, 0, 255)
            if pixels[x, y][2] > 0:
                pixels[x, y] = (255, 255, 255, 255)
    return image

def ConvertImage(image, time, txtQueue):
    image = image.resize((image.width * 6, image.height * 6), Image.ANTIALIAS)
    regionName = 'ddo_bw_' + time + '.bmp'
    image.save(regionName, dpi=(200,200))
    Tesseract(regionName, "ddo_convert_" + time, txtQueue)

def ConvertAndSaveImage(box_filepath, coords, count):
    image = Image.open(box_filepath)
    image = image.crop(coords)
    image = image.resize((image.width * 6, image.height * 6), Image.ANTIALIAS)
    image = ConvertToBW(image)
    name = 'ddo-ddo_font-exp' + count + '.bmp'
    image.save(name, dpi=(200,200))

def Tesseract(input_filename, output_filename, txtQueue):
    # Call the tesseract.exe file with arguments
    args = ['tesseract.exe', input_filename, output_filename, '-l', 'ddo', '-psm', '6']
    # Use the stderr and stdout to ignore tesseract print statements
    proc = subprocess.Popen(args, stderr=subprocess.PIPE, stdout=subprocess.PIPE)
    retcode = proc.wait()
    txtQueue.append(output_filename + ".txt")
    try:
        # Don't want to keep the enlarged box image file since it is pretty big
        os.remove(input_filename)
    except Exception as e:
        print "Couldn't remove temporary files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
        pass

def AppendCombatLog(combatTime, txtQueue, first, save):
    regex = re.compile(r'^\((Combat|Effects)\):.+$', re.IGNORECASE)

    if first[0]:
        if len(txtQueue) > 0:
            with open(txtQueue[0], 'r') as file1:
                initialFile = file1.readlines()

            # Remove junk lines that the OCR failed to parse
            if len(initialFile) > 0 and not regex.match(initialFile[0]):
                initialFile = initialFile[1:]
            # Use list comprehension to remove extra newlines added by OCR
            initialFile = [ x for x in initialFile if not x == '\n' ]

            with open("ddo_combatlog_" + combatTime + ".txt", 'w') as file:
                # Compare the files, get difference deltas
                for line in initialFile:
                    file.write(line)
            first[0] = False
    else:
        if len(txtQueue) >= 2:
            with open(txtQueue[0], 'r') as file1:
                previous = file1.readlines()
            with open(txtQueue[1], 'r') as file2:
                next = file2.readlines()

            # Remove junk lines that the OCR failed to parse
            if len(previous) > 0 and not regex.match(previous[0]):
                previous = previous[1:]
            # Use list comprehension to remove extra newlines added by OCR
            previous = [ x for x in previous if not x == '\n' ]

            # Remove junk lines that the OCR failed to parse
            if len(next) > 0 and not regex.match(next[0]):
                next = next[1:]
            # Use list comprehension to remove extra newlines added by OCR
            next = [ x for x in next if not x == '\n' ]

            with open("ddo_combatlog_" + combatTime + ".txt", 'a') as file:
                # Compare the files, get difference deltas
                for line in Differ().compare(previous, next):
                    # Only want to add the new lines from the next file
                    if line[:2] == "+ ":
                        # Don't want the "+ " identifiers
                        file.write(line[2:])
            if not save:
                try:
                    os.remove(txtQueue[0])
                except Exception as e:
                    print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                    pass
            txtQueue.pop(0)

def ProgramOptions():
    parser = OptionParser()
    parser.add_option("-m", "--mode", dest="mode", default="S", help="Mode of operation. Options: S, B, C, F. S - Screenshot mode, B - Box Image mode, C - Conver to BW, F - Full mode. Default: S", type="string", metavar="MODE")
    parser.add_option("-b", "--box", dest="box", default=(0,0,0,0), help="This takes a series of 4 numbers that are the coordinates of the combat log box. (TopLeftCoord,TopRightCoord,BottomRightCoord,BottomLeftCoord)", metavar="COORDS")
    parser.add_option("-i", "--interval", dest="interval", default=2, help="Interval that the program will take screeshots in seconds. Default: 2.", type="int", metavar="TIME")
    parser.add_option("-s", "--savefiles", dest="saveFiles", action="store_true", help="If present, the progam will not delete the images/text it creates.", metavar="FILES")
    parser.add_option("-p", "--picture", dest="pic", help="Image.", metavar="PIC")
    parser.add_option("-c", "--count", dest="count", help="Count.")
    parser.add_option_group(OptionGroup(parser, "Press Escape to exit. Example run:", "python ddo-logger.py -m F -b (0,0,500,800) -i 2 -s"))
    return parser

def main():
    (options, args) = ProgramOptions().parse_args()

    if options.mode == "S":
        SaveScreenshot()
    elif options.mode == "B":
        SaveBox(literal_eval(options.box))
    elif options.mode == "C":
        timestamp = str(int(time.time()))
        ConvertAndSaveImage(options.pic, literal_eval(options.box), options.count)
    elif options.mode == "F":
        first = [True]
        combatTime = str(int(time.time()))
        txtQueue = []
        while True:
            if msvcrt.kbhit():
                if ord(msvcrt.getch()) == 27:
                    if len(txtQueue) > 0:
                        for file in txtQueue:
                            try:
                                os.remove(file)
                            except Exception as e:
                                print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                                pass
                        files = [i for i in os.listdir(os.getcwd()) if os.path.isfile(os.path.join(os.getcwd(),i)) and 'ddo_bw_' in i]
                        for file in files:
                            try:
                                os.remove(file)
                            except Exception as e:
                                print "Couldn't remove text files. Check the folder and remove them so they don't take up unneeded space on your harddrive."
                                pass
                    break
            AppendCombatLog(combatTime, txtQueue, first, options.saveFiles)
            timestamp = str(int(time.time()))
            thread = threading.Thread(target=Run, args=[literal_eval(options.box), timestamp, txtQueue, options.saveFiles])
            thread.daemon = True
            thread.start()
            #first = False
            time.sleep(options.interval)
    else:
        print("Invalid mode provided.")

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>from IPython.nbformat import current
import github
import time

_missing = object()

def model_to_files(model):
    """
    Converts a IPython notebook model to a github.Gist `files` dict.
    Parameters
    __________
    model : dict
        Notebook model as specified by the NotebookManager. There is
        an additional `__files` dict of the form {filename: file_content}

    Returns
    -------
    files : dict
        {filename: content}
        Note: changed files dict values to be strings.
    """
    files = {}
    name = model['name']
    content = current.writes(model['content'], format=u'json')
    files[name] = content

    __files = model.get('__files', {})
    for fn, content in __files.iteritems():
        files[fn] = content
    return files

def _github_files(files):
    """ wrap basestring content into github.InputFilecontent """
    new_files = {}
    for fn, content in files.items():
        if isinstance(content, basestring):
            content = github.InputFileContent(content)
        new_files[fn] = content
    return new_files

class GistService(object):
    """
    Keeps track of github accounts and gists.
    """
    def __init__(self):
        self.accounts = {}
        self.default = None
        self.gist_cache = {}

    @property
    def hub(self):
        """
        This is for non-user requests. We use a default logged hub
        due to the Rate Limit imposed on anonymous hubs
        """
        return self.accounts[self.default]

    def login(self, login, password):
        if self.default is None:
            self.default = login
        # if account already
        if login in self.accounts:
            print "Alredy logged in"
            return
        hub = github.Github(login, password, user_agent="nbx")
        self.accounts[login] = hub

    def get_gist(self, gist_id, refresh=False):
        if gist_id not in self.gist_cache or refresh:
            gist = self._get_gist(gist_id)
            self.gist_cache[gist_id] = gist
        return self.gist_cache[gist_id]

    def _get_gist(self, gist_id):
        """
        Returns Gist object. If we own the Gist, we return the authenticated
        Gist. Otherwise return a read-only Gist.
        """
        gist = self.hub.get_gist(gist_id)
        owner = self.get_owner(gist)
        if owner is not None and owner is not self.hub:
            print 'grabbed from hub'
            gist = owner.get_gist(gist_id)
        gist = Gister(gist, self)
        return gist

    def create_gist(self, description=None, files=None, public=True, login=None):
        if login is None:
            login = self.default
        if description is None:
            description = "nbx created gist"
        if files is None:
            files = {'empty.txt':'empty file created by nbx'}

        hub = self.accounts[login]
        files = _github_files(files)
        gist = hub.get_user().create_gist(public, files, description)
        assert gist.owner.login == login
        gist = self.get_gist(gist.id)
        return gist

    def is_owned(self, gist):
        """
        Checks if the gist is ownd by an account we manage.
        """
        owner = self.get_owner(gist)
        return owner is not None

    def get_owner(self, gist):
        login = gist.owner.login
        return self.accounts.get(login, None)

    def gist_deleted(self, gist):
        del self.gist_cache[gist.id]

class Gister(object):
    """
    Gister is just to differentiate from github.Gist
    """
    def __init__(self, gist, service):
        self.gist = gist
        self.service = service

        self.id = gist.id

    @property
    def public(self):
        return self.gist.public

    @property
    def description(self):
        return self.gist.description

    @property
    def files(self):
        return self.gist.files

    @property
    def owner(self):
        return self.gist.owner

    def edit(self, description=None, files=None, force=False):
        """
        Wrapper around github.Gist.edit. If nothing has changed
        we will not save unless `force` is true.
        """
        if description is None:
            description = self.description
        if files is None:
            files = {}

        dirty = self._is_dirty(description, files)
        files = _github_files(files)
        if dirty or force:
            self.gist.edit(description, files)
            # at the moment, it looks like sending edit requests
            # too quickly will cause the files dict in the response
            # to be stale. Emailed github about this. For now,
            # putting in a sleep.
            time.sleep(.3)

    def delete(self):
        self.gist.delete()
        self.service.gist_deleted(self)

    def _is_dirty(self, description, files):
        """
        Check whether the description/files would change the current
        gist
        """
        if description != self.gist.description:
            return True

        for fn, content in files.items():
            # new file
            if fn not in self.gist.files:
                return True

            old_content = self.gist.files[fn].content
            if content != old_content:
                return True
        return False

    def save(self, description=None, files=_missing, force=False):
        """
        Similar to edit except it assumes that `files` represents all
        files. So if a file is missing from `files` then it will be deleted.
        """
        if files is _missing:
            raise Exception("files must be explicitly passed in")

        if files is None:
            files = {}

        for fn, fobj in self.gist.files.iteritems():
            if fn not in files:
                files[fn] = None # mark for deletion

        self.edit(description, files=files, force=force)

    def pull(self):
        self.gist = self.service.get_gist(self.gist.id)
<commit_msg>put in a pause for gist api request<commit_after>from IPython.nbformat import current
import github
import time

_missing = object()

def model_to_files(model):
    """
    Converts a IPython notebook model to a github.Gist `files` dict.
    Parameters
    __________
    model : dict
        Notebook model as specified by the NotebookManager. There is
        an additional `__files` dict of the form {filename: file_content}

    Returns
    -------
    files : dict
        {filename: content}
        Note: changed files dict values to be strings.
    """
    files = {}
    name = model['name']
    content = current.writes(model['content'], format=u'json')
    files[name] = content

    __files = model.get('__files', {})
    for fn, content in __files.iteritems():
        files[fn] = content
    return files

def _github_files(files):
    """ wrap basestring content into github.InputFilecontent """
    new_files = {}
    for fn, content in files.items():
        if isinstance(content, basestring):
            content = github.InputFileContent(content)
        new_files[fn] = content
    return new_files

class GistService(object):
    """
    Keeps track of github accounts and gists.
    """
    def __init__(self):
        self.accounts = {}
        self.default = None
        self.gist_cache = {}

    @property
    def hub(self):
        """
        This is for non-user requests. We use a default logged hub
        due to the Rate Limit imposed on anonymous hubs
        """
        return self.accounts[self.default]

    def login(self, login, password):
        if self.default is None:
            self.default = login
        # if account already
        if login in self.accounts:
            print "Alredy logged in"
            return
        hub = github.Github(login, password, user_agent="nbx")
        self.accounts[login] = hub

    def get_gist(self, gist_id, refresh=False):
        if gist_id not in self.gist_cache or refresh:
            gist = self._get_gist(gist_id)
            self.gist_cache[gist_id] = gist
        return self.gist_cache[gist_id]

    def _get_gist(self, gist_id):
        """
        Returns Gist object. If we own the Gist, we return the authenticated
        Gist. Otherwise return a read-only Gist.
        """
        gist = self.hub.get_gist(gist_id)
        owner = self.get_owner(gist)
        if owner is not None and owner is not self.hub:
            print 'grabbed from hub'
            gist = owner.get_gist(gist_id)
        gist = Gister(gist, self)
        return gist

    def create_gist(self, description=None, files=None, public=True, login=None):
        if login is None:
            login = self.default
        if description is None:
            description = "nbx created gist"
        if files is None:
            files = {'empty.txt':'empty file created by nbx'}

        hub = self.accounts[login]
        files = _github_files(files)
        gist = hub.get_user().create_gist(public, files, description)
        assert gist.owner.login == login
        gist = self.get_gist(gist.id)
        return gist

    def is_owned(self, gist):
        """
        Checks if the gist is ownd by an account we manage.
        """
        owner = self.get_owner(gist)
        return owner is not None

    def get_owner(self, gist):
        login = gist.owner.login
        return self.accounts.get(login, None)

    def gist_deleted(self, gist):
        del self.gist_cache[gist.id]

class Gister(object):
    """
    Gister is just to differentiate from github.Gist
    """
    def __init__(self, gist, service):
        self.gist = gist
        self.service = service

        self.id = gist.id

    @property
    def public(self):
        return self.gist.public

    @property
    def description(self):
        return self.gist.description

    @property
    def files(self):
        return self.gist.files

    @property
    def owner(self):
        return self.gist.owner

    def edit(self, description=None, files=None, force=False):
        """
        Wrapper around github.Gist.edit. If nothing has changed
        we will not save unless `force` is true.
        """
        if description is None:
            description = self.description
        if files is None:
            files = {}

        dirty = self._is_dirty(description, files)
        files = _github_files(files)
        if dirty or force:
            self.gist.edit(description, files)
            # at the moment, it looks like sending edit requests
            # too quickly will cause the files dict in the response
            # to be stale. Emailed github about this. For now,
            # putting in a sleep.
            # http://nbviewer.ipython.org/gist/dalejung/9008256a672754df6d7d
            time.sleep(1)

    def delete(self):
        self.gist.delete()
        self.service.gist_deleted(self)

    def _is_dirty(self, description, files):
        """
        Check whether the description/files would change the current
        gist
        """
        if description != self.gist.description:
            return True

        for fn, content in files.items():
            # new file
            if fn not in self.gist.files:
                return True

            old_content = self.gist.files[fn].content
            if content != old_content:
                return True
        return False

    def save(self, description=None, files=_missing, force=False):
        """
        Similar to edit except it assumes that `files` represents all
        files. So if a file is missing from `files` then it will be deleted.
        """
        if files is _missing:
            raise Exception("files must be explicitly passed in")

        if files is None:
            files = {}

        for fn, fobj in self.gist.files.iteritems():
            if fn not in files:
                files[fn] = None # mark for deletion

        self.edit(description, files=files, force=force)

    def pull(self):
        self.gist = self.service.get_gist(self.gist.id)
<|endoftext|>