from django.core.urlresolvers import reverse
from django.contrib import messages
from django.shortcuts import get_object_or_404, render, HttpResponseRedirect, Http404
from django.utils.translation import ugettext as _
from django.views.decorators.cache import cache_control

from haystack.views import FacetedSearchView
from haystack.query import SearchQuerySet

from giteftra2.settings.local import VARIATION_CATEGORIES, SPECIAL_CATEGORIES
from .models import Category, Product, ProductSize, ProductStock, ProductImageDeposit
from .forms import MyFacetedSearchForm, ProductAddToCartForm, QuickAddToCart
from .faceting import get_my_facet_counts, get_my_clean_facet_counts, update_my_facet_counts
from .sorting import sort_products_by
from .quick_add_to_cart import manage_quick_add_to_cart
from .breadcrumbs import show_breadcrumbs_product_details, show_breadcrumbs_categories_by_creator
from .breadcrumbs import show_breadcrumbs_products_by
from cart import cart_funcs

SIZES = ProductSize.get_sizes()

class ShowProductsByCategory(FacetedSearchView):
    def __init__(self):
        sqs = SearchQuerySet().facet('category').facet('creator').facet('material')
        template = 'catalog/show_products_by.html'
        form_class = MyFacetedSearchForm
        # self.cart_form = QuickAddToCart(request=self.request, chosen_prod_stock=None, label_suffix='')
        super(ShowProductsByCategory, self).__init__(template=template, searchqueryset=sqs, form_class=form_class)

    def __call__(self, request, full_slug):
        """
        Captures the whole string after the ***.com/ and stores the slug into the variable self.category_slug.
        Then it tries to get the object from the db using this slug.
        """
        slugs = full_slug.split('/')
        self.category_slug = slugs[-1]
        self.category = get_object_or_404(Category, slug=self.category_slug)
        if not self.category.get_absolute_url().strip('/') == full_slug:
            raise Http404

        request.session.set_test_cookie()

        if request.POST:
            # if a POST request, then call `manage_quick_add_to_cart` method to change layout or remove item
            # from cart or add item in cart (with quantity=1). The message holds the status of the function.
            # `None` or `success` or `warning` are the available messages returned.
            message = manage_quick_add_to_cart(request, self.category)
            if message == u'success':
                return HttpResponseRedirect(reverse('show_cart'))
        else:
            self.cart_form = QuickAddToCart(request=request, chosen_prod_stock=None, label_suffix='')

        show_breadcrumbs_products_by(request, self.category)

        return super(ShowProductsByCategory, self).__call__(request)

    def build_form(self, form_kwargs=None):
        """
        If the user has selected any facets, then these facets are passed (through the GET method initially and then
        through the form_kwargs dict) to the MyFacetedSearchForm class where they are processed to filter the
         products depending on the facets selected.
        """
        if form_kwargs is None:
            self.form_kwargs = {}
        self.form_kwargs['chosen_category'] = self.category.slug
        self.form_kwargs['creator'] = self.request.GET.getlist('creator')
        self.form_kwargs['category'] = self.request.GET.getlist('category')
        self.form_kwargs['material'] = self.request.GET.getlist('material')
        # self.form_kwargs['sizevenus'] = self.request.GET.getlist('sizevenus')
        return super(ShowProductsByCategory, self).build_form(self.form_kwargs)

    def create_response(self):
        """
        Overrides create_response() of SearchView class
        """
        # (paginator, page) = self.build_page()

        context = {
            'query': self.query,
            'form': self.form,
            # 'page': page,
            # 'paginator': paginator,
            'suggestion': None,
        }

        if self.results and hasattr(self.results, 'query') and self.results.query.backend.include_spelling:
            context['suggestion'] = self.form.get_suggestion()

        context.update(self.extra_context())
        return render(self.request, self.template, context)

    def extra_context(self):
        """
        This function overrides from FacetedSearchView.
        We declare our facets (extra['facets']), as we want to present them on the page depending on the category.
        It also takes into account the update of the facets depending on the facet the user checked.
        Also takes into account if the user has used the sorting filter in order to sort (sort_products_by method,
        declared in sorting.py) the (filtered or not) products by the selected value (price etc.)
        Returns a dict.
        """
        sorting = self.request.GET.get('sort', '')
        extra = super(ShowProductsByCategory, self).extra_context()
        if self.form_kwargs['creator'] or self.form_kwargs['category'] or self.form_kwargs['material']:# or self.form_kwargs['sizevenus']:
            dirty_updated_facet_counts = update_my_facet_counts(self.form_kwargs, self.category)
            extra['facets'] = get_my_clean_facet_counts(dirty_updated_facet_counts)
        else:
            dirty_facet_counts = get_my_facet_counts(category=self.category)
            extra['facets'] = get_my_clean_facet_counts(dirty_facet_counts)
        products = sort_products_by(sorting, self.category, self.get_results())
        # trigger whether to show facets and sorting or not
        show_tools = lambda: True if self.category_slug not in SPECIAL_CATEGORIES else False
        # `cart_items` used for determining if a product is in user's cart. We only need product's id and size (if any)
        cart_items = cart_funcs.\
            get_cart_items(self.request).\
            select_related('product_stock__product__id', 'product_stock__size__name_en').\
            values('product_stock__product__id', 'product_stock__size__name_en')
        page_title = self.category.name
        meta_keywords = self.category.meta_keywords
        meta_description = self.category.meta_description
        my_extras = {'category': self.category,
                     'category_slug': self.category_slug,
                     'products': products,
                     'form_kwargs': self.form_kwargs,
                     'sorting': sorting,
                     'show_tools': show_tools,
                     'cart_items': cart_items,
                     'cart_form': self.cart_form,
                     'page_title': page_title,
                     'meta_keywords': meta_keywords,
                     'meta_description': meta_description}
        extra.update(my_extras)
        return extra

class ShowProductsByCreator(ShowProductsByCategory):
    def __init__(self):
        sqs = SearchQuerySet().facet('category').facet('material')
        template = 'catalog/show_products_by.html'
        form_class = MyFacetedSearchForm
        super(ShowProductsByCategory, self).__init__(template=template, searchqueryset=sqs, form_class=form_class)

    def __call__(self, request, creator, category_slug):
        self.creator = creator
        self.category = get_object_or_404(Category, slug=category_slug)
        self.category_slug = category_slug

        request.session.set_test_cookie()

        if request.POST:
            # if a POST request then call `manage_quick_add_to_cart` method to change layout or remove item
            # from cart or add item in cart (with quantity=1). The message holds the status of the function.
            # `None` or `success` or `warning` are the available messages returned.
            message = manage_quick_add_to_cart(request, self.category)
            if message == u'success':
                return HttpResponseRedirect(reverse('show_cart'))
        else:
            self.cart_form = QuickAddToCart(request=request, chosen_prod_stock=None, label_suffix='')

        show_breadcrumbs_products_by(request, self.category, creator=creator)

        return super(ShowProductsByCategory, self).__call__(request)

    def build_form(self, form_kwargs=None):
        if form_kwargs is None:
            self.form_kwargs = {}
        self.form_kwargs['chosen_creator'] = self.creator
        return super(ShowProductsByCreator, self).build_form(self.form_kwargs)

    def extra_context(self):
        sorting = self.request.GET.get('sort', '')
        extra = super(ShowProductsByCategory, self).extra_context()
        if self.form_kwargs['category'] or self.form_kwargs['material']:# or self.form_kwargs['sizevenus']:
            dirty_updated_facet_counts = update_my_facet_counts(self.form_kwargs, self.category)
            extra['facets'] = get_my_clean_facet_counts(dirty_updated_facet_counts)
        else:
            dirty_facet_counts = get_my_facet_counts(
                category=self.category, chosen_creator=self.creator, chosen_category=self.category)
            extra['facets'] = get_my_clean_facet_counts(dirty_facet_counts)
        products = sort_products_by(sorting, self.category, self.get_results())
        # trigger whether to show facets and sorting or not
        show_tools = lambda: True if self.category_slug not in SPECIAL_CATEGORIES else False
        # `cart_items` used for determining if a product is in user's cart. We only need product's id and size (if any)
        cart_items = cart_funcs.\
            get_cart_items(self.request).\
            select_related('product_stock__product__id', 'product_stock__size__name_en').\
            values('product_stock__product__id', 'product_stock__size__name_en')
        page_title = self.category.name
        meta_keywords = self.category.meta_keywords
        meta_description = self.category.meta_description
        my_extras = {'creator': self.creator,
                     'category': self.category,
                     'category_slug': self.category_slug,
                     'products': products,
                     'form_kwargs': self.form_kwargs,
                     'sorting': sorting,
                     'show_tools': show_tools,
                     'cart_items': cart_items,
                     'cart_form': self.cart_form,
                     'page_title': page_title,
                     'meta_keywords': meta_keywords,
                     'meta_description': meta_description}
        extra.update(my_extras)
        return extra

def show_categories_by_creator(request, creator):
    from myutils.context_processors import sidebar_nav_creators

    if request.POST:
        post_data = request.POST.copy()
        if post_data.get('submit') == u'Remove':
            cart_funcs.remove_from_cart(request)

    root_and_sub_cats = sidebar_nav_creators().get(creator.title())
    # Enable breadcrumbs in this view
    show_breadcrumbs_categories_by_creator(request, creator)

    page_title = creator.title()

    return render(request, 'catalog/categories_by_creator.html',
                  {'creator': creator,
                   'root_and_sub_cats': root_and_sub_cats,
                   'page_title': page_title})

# @cache_control(no_cache=True, must_revalidate=True, no_store=True)
def show_product_details(request, product_slug):
    path = request.get_full_path().split('/')
    # detects the category the user is looking (mostly for venus products with various sizes)
    chosen_cat = path[path.index('product')-1]
    product = get_object_or_404(Product, slug=product_slug)
    product_images = ProductImageDeposit.objects.values('large_img', 'thumb_for_large').filter(product=product)
    # get the categories the product belongs to
    # categories = product.categories.filter(is_active=True)
    # set various variables for render
    page_title = product.name
    meta_keywords = product.meta_keywords
    meta_description = product.meta_description
    # get the stock of the selected product (mostly for venus products with various sizes)
    chosen_prod_stock, other_prod_stock, prod_stock_all = get_product_stock(product, chosen_cat)
    request.session.set_test_cookie()

    if request.POST:
        post_data = request.POST.copy()
        cart_form = ProductAddToCartForm(request=request, chosen_prod_stock=None)
        # The `Remove` value is in the cart_box for quick removal
        if post_data.get('submit') == u'Remove':
            cart_funcs.remove_from_cart(request)
        elif post_data.get('add_to_cart_form') == u'Add To Cart':
            cart_form = ProductAddToCartForm(request, post_data, chosen_prod_stock=chosen_prod_stock)
            if cart_form.is_valid():
                msg, type_msg = cart_funcs.add_to_cart(request, chosen_cat, chosen_prod_stock)
                if request.session.test_cookie_worked():
                    request.session.delete_test_cookie()
                if type_msg == u'success':
                    messages.success(request, msg, extra_tags='redirect_only')
                    return HttpResponseRedirect(reverse('show_cart'))
                elif type_msg == u'warning':
                    messages.warning(request, msg)
    else:
        cart_form = ProductAddToCartForm(request=request, chosen_prod_stock=None)

    # Enable breadcrumbs in this view
    show_breadcrumbs_product_details(request, path, product, chosen_cat)
    # `cart_items` used for determining if a product is in user's cart. We only need product's id and size (if any)
    cart_items = cart_funcs.\
        get_cart_items(request).\
        select_related('product_stock__product__id', 'product_stock__size__name_en').\
        values('product_stock__product__id', 'product_stock__size__name_en')
    return render(request, 'catalog/product_details.html',
                  {'product': product,
                   'product_images': product_images,
                   # 'categories': categories,
                   'cart_form': cart_form,
                   'chosen_cat': chosen_cat,
                   'chosen_prod_stock': chosen_prod_stock,
                   'other_prod_stock': other_prod_stock,
                   'prod_stock_all': prod_stock_all,
                   'cart_items': cart_items,
                   'sizes': SIZES,
                   'page_title': page_title,
                   'meta_keywords': meta_keywords,
                   'meta_description': meta_description})

def get_product_stock(product, slug):
    # if `category_slug` == `venus` get all the size-variations (stocks) this Venus-product has
    if slug in VARIATION_CATEGORIES:
        prod_stock_all = product.productstock_set.all()
        return [], [], prod_stock_all

    # if `category_slug` in SIZES get the stock of the product of this particular size (`chosen_prod_stock`)
    # also get all the other size-variations this Venus-product has (`other_prod_stock`)
    if slug in SIZES:
        chosen_prod_stock = product.productstock_set.get(size__name_en=slug.title())
        other_prod_stock = product.productstock_set.exclude(id=chosen_prod_stock.id).all()
        return chosen_prod_stock, other_prod_stock, []

    # if `category_slug` does not involve Venus then it is a no-size product.
    # Return the instance of ProductStock class of the no-size product and
    # empty lists for `other_prod_stock` and `prod_stock_all`
    return product.productstock_set.get(product=product), [], []


import streamlit as st
import pandas as pd
import sys
import chone as one
import chtwo as two

def main():
    # Introduction Page
    st.title('Data Analysis Using SQL & Excel By Wiley')
    st.markdown('My Interpretation Using Dash in place of Excel')

    # A Dictionary of Topics
    ch_dict = {"1-SQL Queries & Concepts": "one",
                "2-Data Exploration": "two"}

    # SideBar Options for selecting Topics
    st.sidebar.checkbox("Topics", True, key=1)
    select = st.sidebar.selectbox('Select a Topic',df_topics)

    # Call the spesific chapter code and content
    sel_ch = ch_dict.get(select)
    #st.markdown(sel_ch)
    class_obj = str_to_class(sel_ch)
    class_obj.content()

# To convert the selected topic str to class obj to call it's content
def str_to_class(str):
    return getattr(sys.modules[__name__], str)

@st.cache(persist=True)
def fetch_data():
    df = pd.read_csv('orders.csv')
    return df

if __name__ == '__main__':
    main()


class DataAugmentation(object):
    """ Data Augmentation.

    Base class for applying common real-time data augmentation.

    This class is meant to be used as an argument of `input_data`. When training
    a model, the defined augmentation methods will be applied at training
    time only. Note that DataPreprocessing is similar to DataAugmentation,
    but applies at both training time and testing time.

    Arguments:
        None

    Parameters:
        methods: `list of function`. The augmentation methods to apply.
        args: A `list` of arguments list to use for these methods.

    """

    def __init__(self):
        self.methods = []
        self.args = []

    def apply(self, batch):
        for i, m in enumerate(self.methods):
            if self.args[i]:
                batch = m(batch, *self.args[i])
            else:
                batch = m(batch)
        return batch