import unittest
from unittest import mock

from mermaid.sequence import SequenceDiagram
from mermaid.sequence.element import Actor, Box, Note, NotePosition, Participant, Rect
from mermaid.sequence.link import ArrowTypes, Link
from mermaid.sequence.logic import Alt, Break, Critical, Loop, Optional, Parallel

class TestActor(unittest.TestCase):
    def test_str(self):
        actor = Actor('John')
        expect_str = '\tactor John\n'
        self.assertEqual(str(actor), expect_str)
        self.assertEqual(actor.id_, 'John')

class TestParticipant(unittest.TestCase):
    def test_str(self):
        participant = Participant('Alice')
        expect_str = '\tparticipant alice as Alice\n'
        self.assertEqual(str(participant), expect_str)

class TestBox(unittest.TestCase):
    def test_str(self):
        # use mocks to create an actor and a participant element
        actor = mock.MagicMock(spec=Actor)
        actor.__str__ = mock.Mock(return_value='\tActor John\n')
        participant = mock.MagicMock(spec=Participant)
        participant.__str__ = mock.Mock(
            return_value='\tparticipant alice as Alice\n')

        box = Box('Test Box', [actor, participant])
        expect_str = """\tbox Test Box
\tActor John
\tparticipant alice as Alice
\tend\n"""
        self.assertEqual(str(box), expect_str)

class TestLink(unittest.TestCase):
    def setUp(self):
        self.source = mock.MagicMock(spec=Actor)
        self.source.id_ = 'John'
        self.target = mock.MagicMock(spec=Participant)
        self.target.id_ = 'Alice'

    def test_str_without_activation_deactivation(self):
        link = Link(self.source, self.target, 'Solid-line', 'Hello World')
        expected_str = '\tJohn->Alice: Hello World\n'
        self.assertEqual(str(link), expected_str)

    def test_str_with_arrow_type_classe(self):
        link = Link(self.source, self.target, ArrowTypes.SOLID_LINE,
                    'Hello World')
        expected_str = '\tJohn->Alice: Hello World\n'
        self.assertEqual(str(link), expected_str)

    def test_str_with_activation(self):
        link = Link(self.source,
                    self.target,
                    'Solid-line',
                    'Hello World',
                    activate_target=True)
        expected_str = '\tJohn->Alice: Hello World\nactivate Alice\n'
        self.assertEqual(str(link), expected_str)

    def test_str_with_deactivation(self):
        link = Link(self.source,
                    self.target,
                    'Solid-line',
                    'Hello World',
                    deactivate_target=True)
        expected_str = '\tJohn->Alice: Hello World\ndeactivate Alice\n'
        self.assertEqual(str(link), expected_str)

class TestNote(unittest.TestCase):
    def setUp(self) -> None:
        self.actor = mock.Mock(spec=Actor)
        self.actor.id_ = 'actor'
        self.participant = mock.Mock(spec=Participant)
        self.participant.id_ = 'participant'
        self.note = 'This is a note'
        return super().setUp()

    def test_note_str_single_element(self) -> None:
        note = Note(self.note, self.actor)
        self.assertEqual(str(note), '\tNote over actor: This is a note\n')

    def test_note_str_multiple_elements(self) -> None:
        note_multiple = Note(self.note, [self.actor, self.participant],
                             NotePosition.OVER)
        self.assertEqual(str(note_multiple),
                         '\tNote over actor,participant: This is a note\n')

    def test_note_position(self) -> None:
        note_left = Note(self.note, self.actor, NotePosition.LEFT_OF)
        self.assertEqual(str(note_left),
                         '\tNote left of actor: This is a note\n')
        note_right = Note(self.note, self.actor, NotePosition.RIGHT_OF)
        self.assertEqual(str(note_right),
                         '\tNote right of actor: This is a note\n')

    def test_note_assertion(self) -> None:
        with self.assertRaises(ValueError):
            Note(self.note, [self.actor, self.participant],
                 NotePosition.LEFT_OF)
        with self.assertRaises(ValueError):
            Note(self.note, [self.actor, self.participant],
                 NotePosition.RIGHT_OF)

class TestLoop(unittest.TestCase):
    def test_loop_str(self) -> None:
        link = mock.MagicMock(spec=Link)
        link.configure_mock(__str__=lambda _: '\tJohn->Alice: Hello World\n')
        loop = Loop('condition', [link])
        self.assertEqual(
            str(loop), '\tloop condition\n\tJohn->Alice: Hello World\n\tend\n')

class TestAlt(unittest.TestCase):
    def setUp(self):
        self.link_1 = mock.MagicMock(spec=Link)
        self.link_1.configure_mock(__str__=lambda _: '\tA-->B: message\n')
        self.link_2 = mock.MagicMock(spec=Link)
        self.link_2.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_one_condition(self):
        alt = Alt({'condition': [self.link_1]})
        excepted_str = '\talt condition\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(alt), excepted_str)

    def test_str_multiple_condition(self):
        alt = Alt({'condition-1': [self.link_1], 'condition-2': [self.link_2]})
        except_str = '\talt condition-1\n\tA-->B: message\n\telse condition-2\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(alt), except_str)

class TestOptional(unittest.TestCase):
    def setUp(self):
        self.link = mock.MagicMock(spec=Link)
        self.link.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_with_one_condition(self):
        optional = Optional('condition', [self.link])
        excepted_str = '\topt condition\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(optional), excepted_str)

    def test_str_with_multiple_condition(self):
        optional = Optional('condition-1', [self.link, self.link])
        excepted_str = '\topt condition-1\n\tA-->B: message\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(optional), excepted_str)

class TestParallel(unittest.TestCase):
    def setUp(self):
        self.link = mock.MagicMock(spec=Link)
        self.link.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_with_one_condition(self):
        parallel = Parallel({'condition': [self.link]})
        excepted_str = '\tpar condition\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(parallel), excepted_str)

    def test_str_with_multiple_condition(self):
        parallel = Parallel({
            'condition-1': [self.link],
            'condition-2': [self.link]
        })
        excepted_str = '\tpar condition-1\n\tA-->B: message\n\tand condition-2\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(parallel), excepted_str)

    def test_str_with_multiple_condition_and_logic(self):
        loop = mock.MagicMock(spec=Loop)
        loop.configure_mock(
            __str__=lambda _: '\tloop condition\n\tA-->B: message\n\tend\n')
        parallel = Parallel({
            'condition-1': [self.link],
            'condition-2': [loop]
        })
        excepted_str = '\tpar condition-1\n\tA-->B: message\n\tand condition-2\n\tloop '
        excepted_str += 'condition\n\tA-->B: message\n\tend\n\tend\n'
        self.assertEqual(str(parallel), excepted_str)

class TestCritical(unittest.TestCase):
    def setUp(self):
        self.link = mock.MagicMock(spec=Link)
        self.link.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_with_one_condition(self):
        critical = Critical('condition-1', [self.link],
                            {'condition-2': [self.link]})
        expected_str = '\tcritical condition-1\n\tA-->B: message\n'
        expected_str += '\toption condition-2\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(critical), expected_str)

    def test_str_with_multiple_condition(self):
        critical = Critical('condition-1', [self.link, self.link], {
            'condition-2': [self.link],
            'condition-3': [self.link]
        })
        expected_str = '\tcritical condition-1\n\tA-->B: message\n'
        expected_str += '\tA-->B: message\n'
        expected_str += '\toption condition-2\n\tA-->B: message\n'
        expected_str += '\toption condition-3\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(critical), expected_str)

class TestBreak(unittest.TestCase):
    def setUp(self) -> None:
        self.link = mock.MagicMock(spec=Link)
        self.link.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_with_one_statement(self):
        break_ = Break('condition', [self.link])
        expected_str = '\tbreak condition\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(break_), expected_str)

    def test_str_with_multiple_statements(self):
        break_ = Break('condition', [self.link, self.link])
        expected_str = '\tbreak condition\n\tA-->B: message\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(break_), expected_str)

class TestRect(unittest.TestCase):
    def setUp(self) -> None:
        self.link = mock.MagicMock(spec=Link)
        self.link.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_with_one_statement(self):
        rect = Rect([self.link], (255, 0, 0))
        expected_str = '\trect rgb(255,0,0)\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(rect), expected_str)

    def test_str_with_multiple_statements(self):
        rect = Rect([self.link, self.link], (255, 0, 0))
        expected_str = '\trect rgb(255,0,0)\n\tA-->B: message\n\tA-->B: message\n\tend\n'
        self.assertEqual(str(rect), expected_str)

    def test_rect_assertion(self):
        with self.assertRaises(ValueError):
            Rect([self.link, self.link], (257, 3, 4, 4))
        with self.assertRaises(ValueError):
            Rect([self.link, self.link], (257, 3, -4))

class TestSequenceDiagram(unittest.TestCase):
    def setUp(self) -> None:
        self.actor_1 = mock.MagicMock(spec=Actor)
        self.actor_1.configure_mock(__str__=lambda _: '\tactor A\n')
        self.actor_2 = mock.MagicMock(spec=Actor)
        self.actor_2.configure_mock(__str__=lambda _: '\tactor B\n')
        self.link_1 = mock.MagicMock(spec=Link)
        self.link_1.configure_mock(__str__=lambda _: '\tA-->B: message\n')
        self.link_2 = mock.MagicMock(spec=Link)
        self.link_2.configure_mock(__str__=lambda _: '\tA-->B: message\n')

    def test_str_without_auto_number(self):
        diagram = SequenceDiagram(
            'Test Diagram',
            [self.actor_1, self.actor_2, self.link_1, self.link_2])
        expected_str = """---
title: Test Diagram
---
sequenceDiagram
\tactor A
\tactor B
\tA-->B: message
\tA-->B: message
"""
        self.assertEqual(diagram.script, expected_str)

    def test_str_with_auto_number(self):
        diagram = SequenceDiagram(
            'Test Diagram',
            [self.actor_1, self.actor_2, self.link_1, self.link_2],
            auto_number=True)
        expected_str = """---
title: Test Diagram
---
sequenceDiagram
\tautonumber
\tactor A
\tactor B
\tA-->B: message
\tA-->B: message
"""
        self.assertEqual(diagram.script, expected_str)


import model
import torch
import numpy as np
from itertools import count
from settings import DEVICE,EPOCHS,GAME,EPISODE_LIFE,CLIP_REWARDS,SCALE,FRAME_STACK,STEPS
from atari_wrappers import wrap_deepmind,make_atari

import time
import matplotlib.pyplot as plt
from utils import log

env=make_atari(GAME)
env=wrap_deepmind(env,episode_life=EPISODE_LIFE,clip_rewards=CLIP_REWARDS,frame_stack=FRAME_STACK,scale=SCALE)

#
np.random.seed(1)
env.seed(0)

log_reward=log(abs_dir_path="F:/github/RL/Atari_DQN/log")
log_epic_step=log(abs_dir_path="F:/github/RL/Atari_DQN/log")
log_loss=log(abs_dir_path="F:/github/RL/Atari_DQN/log")

agent = model.Agent(DEVICE)

total_count=0
start_time=time.time()

done_=False

for i in count():
    if done_:
        break

    state = env.reset()

    epic_reward=0
    for t in count():
        total_count+=1

        if total_count%10000==0:
            end_time=time.time()
            print("1 w frame time: {}".format(end_time-start_time))
            start_time=end_time
            if total_count==STEPS:
                done_=True
                break

        action=agent.select_action(state)

        state_,reward,done,_=env.step(action.item())

        epic_reward+=reward

        reward=torch.tensor([reward],device=DEVICE)

        agent.remember(state,action,state_,reward)

        state=state_

        agent.learn()

        if done:
            log_reward.add(epic_reward)
            log_epic_step.add(t+1)

            print("episode: {} reward: {} step: {} memory: {}".format(i,epic_reward,t+1,len(agent.memory)))

            break

log_loss.add(agent.loss_hist)

fig=plt.figure()

ax1=fig.add_subplot(221)
ax2=fig.add_subplot(222)
ax3=fig.add_subplot(223)
ax1.plot(np.arange(0,len(log_loss.buf)),log_loss.buf)
ax2.plot(np.arange(0,len(log_reward.buf)),log_reward.buf)
ax3.plot(np.arange(0,len(log_epic_step.buf)),log_epic_step.buf)

plt.show()

log_epic_step.write(file_name="epic_step_3m.csv")
log_reward.write(file_name="reward_3m.csv")
log_loss.write(file_name="loss_3m.csv")


"""Products serializers"""

# Utils
from fyra.utils.serializers import FyraBaseSerializer

# Models
from fyra.products.models.products import Product
from fyra.products.models.categories import Category
from fyra.products.models.units import Unit

# Django Rest Framework
from rest_framework import serializers

class ProductSerializer(FyraBaseSerializer):
    """
    Inherits BaseSerializers from Utils that allow us to dont repeat
    code. Only need change model.
    """
    class Meta(FyraBaseSerializer.Meta):
        model = Product

    def to_representation(self, instance):
        view_api = {
            'uuid': instance.uuid,
            'name': instance.name,
            'category': instance.category.name,
            'unit': instance.unit.abbreviation,
            'price': instance.price
        }
        return view_api

    def validate_category(self, data):
        """
        Valid if the category belongs to the user's company
        """
        company = self.context['request'].user.company_name
        pk = data.pk
        if not Category.objects.filter(pk=pk, company=company).exists():
            raise serializers.ValidationError("Category doesn't exist")
        return data

    def validate_unit(self, data):
        """
        Valid if the unit belongs to the user's company
        """
        company = self.context['request'].user.company_name
        pk = data.pk
        if not Unit.objects.filter(pk=pk, company=company).exists():
            raise serializers.ValidationError("Category doesn't exist")
        return data


import numpy as np
import torch
import torch.nn as nn
from ..networks.generic_network import GenericNetwork
from ..networks.lstm import LSTMNetwork
from ..networks.multilayer_perceptron import MLPNetwork
from torch.distributions.normal import Normal

class ContinuousActor(GenericNetwork):
    def __init__(self, device_id: int = 0):
        super().__init__(device_id=device_id)

    def __new__(cls, *args, **kwargs):
        if cls is ContinuousActor:
            raise TypeError(
                f"'{cls.__name__}' should not be directly instantiated. "
                + f"Try '{cls.__name__}MLP' or '{cls.__name__}LSTM' instead."
            )
        return object.__new__(cls)

    def set_up_parameters(
        self,
        shape: tuple,
        learning_rate: float,
        starting_std_dev: float,
        clip_epsilon: float,
        entropy_coefficient: float,
    ):
        self.clip_epsilon = clip_epsilon
        self.entropy_coefficient = entropy_coefficient
        num_actions = shape[-1]
        self.log_standard_deviation = nn.Parameter(
            torch.ones((1, num_actions), device=self.device) * np.log(starting_std_dev)
        )
        self.create_optimizer(learning_rate)

    def get_distribution(
        self, inputs: torch.Tensor, should_detach: bool = False
    ) -> Normal:
        means = self.forward(inputs)
        if should_detach:
            means = means.detach()
        standard_deviations = torch.exp(self.log_standard_deviation)
        return Normal(means, standard_deviations)

    def gradient_ascent_step(
        self,
        states: torch.Tensor,
        actions: torch.Tensor,
        log_probs: torch.Tensor,
        advantages: torch.Tensor,
    ) -> None:
        self.optimizer.zero_grad()
        loss = self.compute_actor_loss(states, actions, log_probs, advantages)
        loss.backward()
        self.optimizer.step()

    def compute_actor_loss(
        self,
        states: torch.Tensor,
        actions: torch.Tensor,
        log_probs: torch.Tensor,
        advantages: torch.Tensor,
    ) -> torch.Tensor:
        distribution = self.get_distribution(states)
        new_log_probs = distribution.log_prob(actions)
        prob_ratios = torch.exp(new_log_probs - log_probs)
        first_term = prob_ratios * advantages
        second_term = (
            torch.clamp(prob_ratios, 1 - self.clip_epsilon, 1 + self.clip_epsilon)
            * advantages
        )
        clip_objective = torch.minimum(first_term, second_term)
        mean_clipped_objective = clip_objective.mean().mean()
        entropy = distribution.entropy().mean()
        actor_objective = mean_clipped_objective + self.entropy_coefficient * entropy
        return -actor_objective

class ContinuousActorMLP(MLPNetwork, ContinuousActor):
    def __init__(
        self,
        shape: tuple,
        learning_rate: float = 3e-4,
        starting_std_dev: float = 1.0,
        clip_epsilon: float = 0.2,
        entropy_coefficient: float = 0.01,
        layer_activation=nn.ELU,
        output_activation=nn.Tanh,
        device_id: int = 0,
    ):
        super().__init__(
            shape=shape,
            layer_activation=layer_activation,
            output_activation=output_activation,
            device_id=device_id,
        )
        self.set_up_parameters(
            shape, learning_rate, starting_std_dev, clip_epsilon, entropy_coefficient
        )

class ContinuousActorLSTM(LSTMNetwork, ContinuousActor):
    def __init__(
        self,
        shape: tuple,
        sequence_length: int,
        learning_rate: float = 3e-4,
        starting_std_dev: float = 1.0,
        clip_epsilon: float = 0.2,
        entropy_coefficient: float = 0.01,
        output_activation=nn.Tanh,
        device_id: int = 0,
    ):
        super().__init__(
            shape=shape,
            sequence_length=sequence_length,
            output_activation=output_activation,
            device_id=device_id,
        )
        self.set_up_parameters(
            shape, learning_rate, starting_std_dev, clip_epsilon, entropy_coefficient
        )


from joblib import *

import streamlit as st
model_pred=load('bmw_sales_prediction.pkl')

def main():
    st.title('Welcome to the  BMW car sale ')
    st.image('bmw.jpg')
    model=['1 series','2 series','3 series','4 series',
           '5 series','6 series','7 series','8 series',
           'M2','M3','M4','M5','X1','X2','X3','X4','X5',
           'X6','X7','Z4','i8','i3']
    model_op=[i for i in range(24) if i!=12 and i!=20]

    model_series=st.selectbox('Enter Model',model_op,format_func=lambda x:model[x] if x<=11 else(model[x-1] if x>11 and x<=19 else model[x-2]))

    year=[i for i in range(1996,2021)]
    model_year=st.selectbox('Select model year',year,format_func=lambda x:x)

    trans=['Automatic','Mannual','Semi-Automatic']
    gen=[i for i in range(len(trans))]
    model_trans=st.selectbox('Select Transmission Type',gen,format_func=lambda x:trans[x])

    mil=st.text_input('Enter Mileage of your car')

    fuel=['Diesel','Electric','Hybrid','Other','Petrol']
    gen_1=[i for i in range(len(fuel))]
    fuel_type=st.selectbox('Select fuel type',gen_1,format_func=lambda x:fuel[x])

    distance=st.text_input('Enter Kms of your car')

    size=[0.6,1,1.5,1.6,1.9,2.0,2.2,2.5,2.8,3.0,3.2,4.0,4.4,5.0,6.6]
    engine_size=st.selectbox('Select Engine size',size)
    prediction=st.button('Predict')

    if prediction:
        array=[model_series,model_year,model_trans,mil,fuel_type,distance,engine_size]
        data=[float(i) for i in array]
        pred=model_pred.predict([data])
        st.success('The price of your car will be {}'.format(pred[0]))

if __name__ == '__main__':
    main()


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import cvxopt as cvx
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import scipy.spatial.distance as dt

# In[2]:

# read data into memory
images_data_set = np.genfromtxt("hw06_images.csv", delimiter = ",")
labels_data_set = np.genfromtxt("hw06_labels.csv", delimiter = ",").astype(int)

# In[3]:

# divide data set into two parts: training set and test set
x_training = images_data_set[0:1000, :]
x_test = images_data_set[1000:, :]
y_training = labels_data_set[0:1000]
y_test = labels_data_set[1000:]

# In[4]:

# get number of classes
K = np.max(y_training)

# get number of samples and number of features
N_training = len(y_training)
N_test = len(y_test)

# In[5]:

# define Gaussian kernel function
def gaussian_kernel(X1, X2, s):
    D = dt.cdist(X1, X2)
    K = np.exp(-D**2 / (2 * s**2))
    return(K)

# In[6]:

def SVM(Y, C):
    yyK = np.matmul(Y[:,None], Y[None,:]) * K_training

    P = cvx.matrix(yyK)
    q = cvx.matrix(-np.ones((N_training, 1)))
    G = cvx.matrix(np.vstack((-np.eye(N_training), np.eye(N_training))))
    h = cvx.matrix(np.vstack((np.zeros((N_training, 1)), C * np.ones((N_training, 1)))))
    A = cvx.matrix(1.0 * Y[None,:])
    b = cvx.matrix(0.0)

    # use cvxopt library to solve QP problems
    result = cvx.solvers.qp(P, q, G, h, A, b)
    alpha = np.reshape(result["x"], N_training)
    alpha[alpha < C * epsilon] = 0
    alpha[alpha > C * (1 - epsilon)] = C

    # find bias parameter
    support_indices, = np.where(alpha != 0)
    active_indices, = np.where(np.logical_and(alpha != 0, alpha < C))
    w0 = np.mean(Y[active_indices] * (1 - np.matmul(yyK[np.ix_(active_indices, support_indices)], alpha[support_indices])))

    return alpha, w0

# In[7]:

s = 10
C = 10
epsilon = 1e-3

# calculate Gaussian kernel
K_training = gaussian_kernel(x_training, x_training, s)

SVM_alphas = []
SVM_w0s = []
for i in range (K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    alpha, w0 = SVM(y_adjusted, C)
    SVM_alphas.append(alpha)
    SVM_w0s.append(w0)
SVM_alphas = np.array(SVM_alphas)
SVM_w0s = np.array(SVM_w0s)

# In[8]:

# calculate predictions on training samples
scores = []
for i in range(K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    f_predicted = np.matmul(K_training, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
    f_predicted = np.squeeze(f_predicted)
    scores.append(f_predicted)
scores = np.array(scores)

y_predicted = np.argmax(scores, axis=0)
y_predicted = y_predicted + 1

# calculate confusion matrix
confusion_matrix = pd.crosstab(np.reshape(y_predicted, N_training), y_training, rownames = ['y_predicted'], colnames = ['y_train'])
print(confusion_matrix)

# In[9]:

K_test = gaussian_kernel(x_test, x_training, s)

# calculate predictions on test samples
scores = []
for i in range(K):
    y_adjusted = np.ones(y_training.shape[0])
    y_adjusted[y_training != i+1] = -1
    f_predicted = np.matmul(K_test, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
    f_predicted = np.squeeze(f_predicted)
    scores.append(f_predicted)
scores = np.array(scores)

y_predicted = np.argmax(scores, axis=0)
y_predicted = y_predicted + 1

# calculate confusion matrix
confusion_matrix = pd.crosstab(np.reshape(y_predicted, N_test), y_test, rownames = ['y_predicted'], colnames = ['y_test'])
print(confusion_matrix)

# In[10]:

def calc_accuracy(C):
    SVM_alphas = []
    SVM_w0s = []
    for i in range (K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        alpha, w0 = SVM(y_adjusted, C)
        SVM_alphas.append(alpha)
        SVM_w0s.append(w0)
    SVM_alphas = np.array(SVM_alphas)
    SVM_w0s = np.array(SVM_w0s)

    # calculate predictions on training samples
    scores = []
    for i in range(K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        f_predicted = np.matmul(K_training, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
        f_predicted = np.squeeze(f_predicted)
        scores.append(f_predicted)
    scores = np.array(scores)

    y_predicted = np.argmax(scores, axis=0)
    y_predicted = y_predicted + 1

    train_accuracy = np.sum(y_predicted == y_training) / len(y_training)

    # calculate predictions on test samples
    scores = []
    for i in range(K):
        y_adjusted = np.ones(y_training.shape[0])
        y_adjusted[y_training != i+1] = -1
        f_predicted = np.matmul(K_test, y_adjusted[:,None] * SVM_alphas[i][:,None]) + SVM_w0s[i]
        f_predicted = np.squeeze(f_predicted)
        scores.append(f_predicted)
    scores = np.array(scores)

    y_predicted = np.argmax(scores, axis=0)
    y_predicted = y_predicted + 1

    test_accuracy = np.sum(y_predicted == y_test) / len(y_test)

    return train_accuracy, test_accuracy

# In[11]:

training_accuracy = []
test_accuracy = []
for C in [1e-1, 1e0, 1e1, 1e2, 1e3]:
    training, test = calc_accuracy(C)
    training_accuracy.append(training)
    test_accuracy.append(test)

# In[12]:

fig = plt.figure(figsize = (8,6))

plt.plot([1e-1, 1e0, 1e1, 1e2, 1e3], training_accuracy, "b.-", label = "training", markersize = 10)
plt.plot([1e-1, 1e0, 1e1, 1e2, 1e3], test_accuracy, "r.-", label = "test", markersize = 10)

plt.xscale("log")
plt.xlabel("Regularization parameter (C)")
plt.ylabel("Accuracy")
plt.legend(loc = "upper left")
plt.show()


import random
import xml.etree.ElementTree as ET
from os import path
from sys import setrecursionlimit

from cv2 import resize, imread, INTER_NEAREST
from PIL import Image, ImageDraw

setrecursionlimit(10000)

# 1 is max detail
detail = 0.5

# for old CoSpace Versions
FieldA = "../../../../../store/media/Rescue/Map/Sec/Design/FieldA"
FieldB = "../../../../../store/media/Rescue/Map/Sec/Design/FieldB"
FieldFD = "../../../../../store/media/Rescue/Map/Sec/Design"

cospace_version = "4.0.9"  # "2.6.2" #

# for CoSpace 2.6.2+
if cospace_version == "2.6.2":
    FieldA = "../../../../../store/media/CS.C/RSC/Map/Design/FieldA"
    FieldB = "../../../../../store/media/CS.C/RSC/Map/Design/FieldB"
    FieldFD = "../../../../../store/media/CS.C/RSC/Map/Design"

def convert_arr_to_area_vector_string(arr):
    if len(arr) == 0:
        return "{};"
    return "{\n\t" + str(arr).replace("[", "{").replace("]", "}").replace(" ", "").replace("{{{", "{{") \
        .replace("}},", "}}),\n\t").replace("{{", "Area({{").replace("}}}", "}})}") + ";"

def collectible_type_switch(obj_type):
    # format: b, g, r
    switcher = {
        "Object_Red": 0,
        "Object_Green": 1,
        "Object_Black": 2
    }
    return switcher.get(obj_type, 0)

def find_color_points(world, field):
    arr = []

    field_obj_collection = world.find("FieldObjects")
    field_objs = field_obj_collection.findall("FOBJ")

    for fobj in field_objs:
        obj_type = fobj.find("ObjType").text
        if obj_type in ["Object_Black", "Object_Red", "Object_Green"]:
            obj_type = collectible_type_switch(obj_type)
            cx = float(fobj.find("CX").text)
            cy = float(fobj.find("CY").text)
            arr.append(Collectible(obj_type, cx, cy, field))

    return arr

def remove_dif_pixels(pixels, val):
    """A method to check if all pixels in Pixels have the value val. It returns all different pixels"""

    different_pixels = []

    #  loop through every pixel in pixels
    for struct in pixels:
        for pixel in struct:
            # value check
            if abs(pixel.val) != val:
                #  remove pixel right away
                struct.remove(pixel)
                print("removed")
                different_pixels.append(pixel)

    return different_pixels

def draw_polygons(draw, polygons, scale, starting_hue=0, sat=255, lum=255, hue_dif=40):
    """draw the boundaries of each polygon in polygons"""

    x_off = scale
    y_off = scale

    # j is to differentiate each polygon from each other
    j = 0
    for polygon in polygons:
        for k in range(0, len(polygon)):
            p1 = polygon[k]
            p2 = polygon[(k + 1) % (len(polygon))]
            for p in get_line(p1.x, p1.y, p2.x, p2.y):
                coord = (
                    p[0] * scale - scale / 2 + x_off, p[1] * scale - scale / 2 + y_off,
                    p[0] * scale + scale / 2 + x_off, p[1] * scale + scale / 2 + y_off)
                draw.rectangle(coord, width=scale, fill=(starting_hue + j * hue_dif, sat, lum))

            coord = (
                p1.x * scale - scale / 2 + x_off, p1.y * scale - scale / 2 + y_off,
                p1.x * scale + scale / 2 + x_off, p1.y * scale + scale / 2 + y_off)
            draw.rectangle(coord, width=scale, fill=(2 * starting_hue + j * hue_dif, sat + 50, lum))

        j += 1

#####################
#       points      #
#####################

class Point:

    def __init__(self, field, x, y):
        """A simple Point in a 2D grid"""
        # coords
        self.x = x
        self.y = y

        #  The object where this Point is saved in
        self.field = field

    def __repr__(self):
        """Returns Point as typical C++ pair coord"""
        return "{%s,%s}" % (self.x, self.field.height - self.y)

    def __str__(self):
        return "[%s | %s]" % (self.x, self.field.height - self.y)

def color_switch(color):
    # format: b, g, r
    switcher = {
        tuple([0, 0, 0]): 1,  # wall
        tuple([151, 186, 221]): 1,
        tuple([131, 177, 244]): 1,
        tuple([0, 255, 255]): 0,  # trap (only use the blue in the middle)
        tuple([151, 85, 47]): 2,
        tuple([196, 114, 68]): 2,
        tuple([166, 166, 166]): 3,  # swamp
        tuple([0, 153, 255]): 4,  # deposit
        tuple([0, 102, 255]): 4,
        tuple([240, 176, 0]): 5  # water
    }
    return switcher.get(color, 0)

class Pixel(Point):
    """A type of point used to represent the actual image"""

    def __init__(self, field, x, y, color):
        super().__init__(field, x, y)

        #  value of the Pixel (easier to handle than rgb colors)
        self.val = color_switch(tuple([color[0], color[1], color[2]]))

    def __str__(self):
        """Returns Pixel as coord including it's val"""

        return "Pixel at [%s | %s] has value %s" % (self.x, self.field.height - self.y, self.val)

def get_line(x1, y1, x2, y2):
    """Returns all Points on a line from (x1, y1) to (x2, y2)"""

    points = []
    issteep = abs(y2 - y1) > abs(x2 - x1)
    if issteep:
        x1, y1 = y1, x1
        x2, y2 = y2, x2
    rev = False
    if x1 > x2:
        x1, x2 = x2, x1
        y1, y2 = y2, y1
        rev = True
    deltax = x2 - x1
    deltay = abs(y2 - y1)
    error = int(deltax / 2)
    y = y1
    ystep = None
    if y1 < y2:
        ystep = 1
    else:
        ystep = -1
    for x in range(x1, x2 + 1):
        if issteep:
            points.append((y, x))
        else:
            points.append((x, y))
        error -= deltay
        if error < 0:
            y += ystep
            error += deltax
    # Reverse the list if the coordinates were reversed
    if rev:
        points.reverse()
    return points

class Boundary(Point):
    """A type of Point to represent corners. Each corner is bound to a wall boundary and vise versa"""

    def __init__(self, field, match: []):
        super().__init__(field, match[0].x, match[0].y)

        self.match = match
        self.connections = []  # max 2

    def find_connections(self, corners):

        # NOTE: (x, y) rotated around (0, 0) by 90° (clockwise) becomes (y, -x)

        #  value (color)
        val = self.field.img_arr[self.y][self.x].val

        connections = (self.match[0], self.match[0])

        direction_x, direction_y = self.match[2].x - self.x, self.match[2].y - self.y

        side = -1  # 1 = hug right side, -1 = hug left side

        if self.match[1].x - self.x != self.match[1].y - self.y:
            side = 1

        for connection in connections:

            x, y = self.x, self.y
            connection_found = False

            while not connection_found:

                # coords in front of the current position
                front_x, front_y = x + direction_x, y + direction_y

                # the pixel in front of the last checked pixel --> pixel that is now checked
                p = self.field.get_pixel(front_x, front_y)
                if p and p.val == val:  # if p is a valid next step

                    # check if p is a corner
                    for corner in corners:
                        if corner is not self:
                            if p is corner.match[0]:
                                self.connections.append(corner)
                                connection_found = True
                                break

                    # clockwise turn if side == 1 otherwise counter clockwise
                    # the pixel on one side of the current pixel
                    direction_x, direction_y = side * direction_y, -side * direction_x

                    x, y = front_x, front_y

                else:
                    #  counter-clockwise turn (if side = 1)
                    direction_x, direction_y = -side * direction_y, side * direction_x

            #  for the other connection
            direction_x, direction_y = self.match[3].x - self.x, self.match[3].y - self.y
            side *= -1

    def show(self, draw, scale, show_connections=True,
             hue=90, sat=255, lum=255):
        """This function displays the corner in the given draw object"""
        random.seed()
        visible_hue = random.randint(0, 255)
        boundary_hue = random.randint(100, 360)

        x_off = scale + int(scale / 2)
        y_off = scale + int(scale / 2)

        #  The upper left and bottom right corner coords of the corner itself
        coord = (
            self.x * scale - scale / 2 + x_off, self.y * scale - scale / 2 + y_off,
            self.x * scale + scale / 2 + x_off, self.y * scale + scale / 2 + y_off)

        #  draw a rectangle at the position. Color = green if reachable; red if not
        draw.rectangle(coord, width=scale,
                       fill=(hue, sat, lum))
        #  draw a number above the corner to display it's position in the ordered array

        draw.text((self.x * scale + x_off, self.y * scale - y_off), str(len(self.connections)), stroke_fill=2,
                  stroke_width=2)

        #  line to boundaries
        if show_connections:
            for connection in self.connections:
                coord = (
                    self.x * scale + x_off, self.y * scale + + y_off,
                    connection.x * scale + x_off, connection.y * scale + + y_off)
                draw.line(coord, fill=(boundary_hue, 255, 255, 50))

            #  line to boundaries
            if show_connections:
                for match in self.match:
                    coord = (
                        self.x * scale + x_off, self.y * scale + + y_off,
                        match.x * scale + x_off, match.y * scale + + y_off)
                    draw.line(coord, fill=(boundary_hue, 255, 255, 100))

def __str__(self):
    """Returns corner as coord including how many other corners this corner can see. For Debugging"""

    return "Boundary at [%s | %s]" % (self.x, self.field.height - self.y)

class Collectible(Point):
    def __init__(self, t, x, y, field):
        super().__init__(float(x), float(y), field)
        """A type of Point to represent Collectible points"""

        # x and y are real coordinates in the simulator. Not the coordinates used by the robot
        #  Virtual coordinates are the ones used by the robot
        #  world 1 is 90 cm bigger in both directions (x, y)
        self.virtual_x = round(-100 * float(x) + field.width / 2)
        self.virtual_y = round(100 * float(y) + field.height / 2)

        #  The color (red = 0; cyan/green = 1; black = 2)
        self.t = t

    def __str__(self):
        """Returns Collectible as coord including it's color. For Debugging"""
        return "Collectible at [%s | %s] with color %s" % (self.x, self.y, self.t)

    def __repr__(self):
        """Returns Collectible as typical C++ pair coord"""
        return "{{%s,%s},%s}" % (self.virtual_x, self.virtual_y, self.t)

###########################
#       FieldObject       #
###########################

class FieldObject:

    def __init__(self, _dir):
        """A class to collect one single map as Pixels by converting _dir/Background.bmp"""

        global detail

        # The array all pixels are stored in
        self.img_arr = []

        # The dimensions of the map
        self.width, self.height = 0, 0

        # if _dir doesn't exist the user has to change the input files
        if path.isdir(_dir):

            # resize the image for less detail but less ram usage and duration
            t_img = None
            if path.isfile(_dir + "\Background(complete).bmp"):
                t_img = resize(imread(_dir + "/Background(complete).bmp"), None, fx=detail, fy=detail,
                               interpolation=INTER_NEAREST)
            else:
                t_img = resize(imread(_dir + "/Background.bmp"), None, fx=detail, fy=detail,
                               interpolation=INTER_NEAREST)

            # img = Image.fromarray(t_img)
            # img.show("orig")

            # convert the Image into pixels
            self.img_arr = [[Pixel(self, i, j, t_img[j][i]) for i in range(len(t_img[j]))] for j in
                            range(len(t_img))]

            # set the dimensions
            self.width = len(self.img_arr[0])
            self.height = len(self.img_arr)
        else:
            # Error
            print("Path \"" + _dir + "\" does not exist")

    def get_pixel(self, x, y):
        if 0 <= x < self.width and 0 <= y < self.height:
            return self.img_arr[y][x]
        return None

    def flood_fill(self, i, j, old_val, new_val, store=False):
        """This method is used to fill a region of Pixels with same value.
            It can also be used to just store them in an array by setting an arr"""

        #  Normal recursive 8-way floodfill
        if not store:
            #  Check if pixel.val equals the starting val
            if self.img_arr[j][i].val == old_val:

                #  Replace the value
                self.img_arr[j][i].val = new_val

                #  invoke on all neighboring pixels with same parameters but different coords
                for n in range(j - 1 if j - 1 >= 0 else 0, j + 2 if j + 1 < self.height else self.height):
                    for m in range(i - 1 if i - 1 >= 0 else 0, i + 2 if i + 1 < self.width else self.width):
                        #  store=False is standard and therefore doesn't have to be set
                        self.flood_fill(m, n, old_val, new_val)

        #  non recursive 8-way flood-fill
        else:

            #  q (short for queue) used for a non recursiv flood fill
            q = [self.img_arr[j][i]]

            #  To avoid setting values go back
            stored = []

            while len(q) > 0:

                #  for each neighbor (8x8)
                for n in range(q[0].y - 1 if q[0].y - 1 >= 0 else 0,
                               q[0].y + 2 if q[0].y + 1 < self.height else self.height):
                    for m in range(q[0].x - 1 if q[0].x - 1 >= 0 else 0,
                                   q[0].x + 2 if q[0].x + 1 < self.width else self.width):

                        #  p is the pixel that might be added
                        p = self.img_arr[n][m]
                        #  print("Checking if %s is valid" % n)

                        #  check if n is or was in q to prevent double checks
                        if p in q or p in stored:
                            # print("\t invalid (already look at it)")
                            continue

                        #  check if the values match
                        if p.val != old_val:
                            # print("\t invalid (value doesn't match)")
                            continue

                        #  The pixels value is updated
                        p.val = new_val

                        #  this pixel can be added to the queue
                        q.append(p)

                #  append to store array and remove from queue

                stored.append(q[0])
                q.pop(0)

            return stored
        return None

    def get_adjacent(self, i, j):
        """Function to get all adjacent Pixels of the same type"""

        #  Use the flood_fill function with store=true to
        old_val = self.img_arr[j][i].val
        return self.flood_fill(i, j, old_val, old_val, store=True)

    def expand(self, i, j, r):
        """Function to expand the type of every pixel of the same type around i, j by r"""

        # Collect all adjacent pixels
        pixels = self.get_adjacent(i, j)

        # Array for storing all new added Pixels
        changed_pixels = []

        # Expand for every collected pixel
        for p in pixels:

            # set val in radius r
            for n in range(p.y - r if p.y - r >= 0 else 0, p.y + r + 1 if p.y + r < self.height else self.height):
                for m in range(p.x - r if p.x - r >= 0 else 0, p.x + r + 1 if p.x + r < self.width else self.width):

                    # disable expanding over walls
                    if abs(self.img_arr[n][m].val) != 1:
                        self.img_arr[n][m].val = p.val
                        changed_pixels.append(self.img_arr[n][m])

        return changed_pixels

    def expand_all(self, val, r, pixels=None):
        """This function expands every pixel of a certain type by r"""
        if pixels is None:
            pixels = self.collect_pixels(val)

        # Array for storing all new added Pixels
        changed_pixels = []

        # expand for every struct in pixels
        for struct in pixels:
            changed_pixels.append(self.expand(struct[0].x, struct[0].y, r))

        return changed_pixels

    def collect_pixels(self, val):
        """This function returns every pixel of type val in self.img_arr in form of structs"""
        """NOTE: This function does not work on deactivated pixels and only collects acivated pixels"""

        pixels = []

        disabled_val = -val

        # loop through the complete array
        for j in range(self.height):
            for i in range(self.width):
                if self.img_arr[j][i].val == val:

                    # get all adjacent pixels to put them into a struct
                    pixels.append(self.get_adjacent(i, j))

                    # disable all pixels for further inspections
                    for p in pixels[len(pixels) - 1]:
                        p.val = disabled_val

        # activate disabled pixels again
        for struct in pixels:
            for p in struct:
                p.val = val

        return pixels

    def check_pattern(self, pixel, patterns=None, val=1):
        """Function to check if the pattern matches.
            There are default patterns and the default value the pattern is referring to is 1"""

        #  Use default pattern if no extra pattern is provided
        if patterns is None:
            patterns = [[1, 0, 1, 1], [1, 0, 0, 0]]

        #  used to store a 2x2 square of pixels that later is compared to the pattern
        squares = []

        # check the diganoal neighbors if there outside. If theyre outside add a Pixel with no val
        steps = ((1, 1), (1, -1), (-1, 1), (-1, -1))
        for step in steps:
            x = pixel.x + step[0]
            y = pixel.y + step[1]

            #  if everything is inside the boundarys everything is good
            if self.width > x >= 0 and self.height > y >= 0:
                squares.append((pixel, self.img_arr[y][x], self.img_arr[pixel.y][x], self.img_arr[y][pixel.x]))

            #  if x is outside and y is inside pixels that would be out of bounds are generated
            elif (self.width <= x or x < 0) and self.height > y >= 0:
                squares.append(
                    (pixel, Pixel(None, x, y, [255, 255, 255]), Pixel(None, x, pixel.y, [255, 255, 255]),
                     self.img_arr[y][pixel.x]))

            #  if y is outside and x is inside pixels that would be out of bounds are generated
            elif self.width > x >= 0 and (self.height <= y or y < 0):
                squares.append(
                    (pixel, Pixel(None, x, y, [255, 255, 255]), Pixel(None, pixel.x, y, [255, 255, 255]),
                     self.img_arr[pixel.y][x]))

            #  if x and y are outside pixels that would be out of bounds are generated
            else:
                squares.append(
                    (pixel, Pixel(None, x, y, [255, 255, 255]), Pixel(None, pixel.x, y, [255, 255, 255]),
                     Pixel(None, x, pixel.y, [255, 255, 255])))

        #  check for matches
        matches = []
        for square in squares:

            # Since the patterns is a int(bool) array use val to compare
            vals = [int(abs(p.val) == abs(val)) for p in square]

            # Compare the patterns
            if vals in patterns:
                matches.append(square)

        return matches

    def avg(self, pixels):
        """This function calculates the average point of all adjacent Pixels to the Pixel at i, j"""

        # Calculate the average
        if len(pixels) > 0:
            x = 0
            y = 0
            for n in pixels:
                x += n.x
                y += n.y

            # also return the pixels if they need to be deactivated
            return [int(x / len(pixels)), int(y / len(pixels))]

        return None

    def __str__(self):
        s = ""
        for row in self.img_arr:
            s += str(row) + "\n"
        return s

def get_corners(field: FieldObject, pixels: [Pixel]):
    corners = []

    #  collect all possible corners by pattern recognition
    for pixel in pixels:
        matches = field.check_pattern(pixel, val=pixel.val)
        for match in matches:
            corners.append(Boundary(field, match))

        #  collect all corners that are unescessary
    corners_to_delete = []
    checking_neighbour_steps = [[1, 1], [-1, 1]]
    for corner in corners:
        for step in checking_neighbour_steps:
            neighbour_count = 0
            for c_corner in corners:
                if c_corner.x == corner.x + step[0] and c_corner.y == corner.y + step[1]:
                    neighbour_count += 1
                elif c_corner.x == corner.x - step[0] and c_corner.y == corner.y - step[1]:
                    neighbour_count += 1

                if neighbour_count >= 2:
                    corners_to_delete.append(corner)

    #  delete them
    for corner in corners_to_delete:
        if corner in corners:
            corners.remove(corner)

    for corner in corners:
        corner.find_connections(corners)

    return corners

def order_corners(corners):
    """Returns an array of corners in order, so that "neighboring" corners in the array represent a wall connection"""

    ordered = []

    #  corners are already ordered by structs
    for corner_struct in corners:

        #  print("ordering cornerstruct: " + str(corner_struct))

        #  open_corners are all unsorted corners
        open_corners = corner_struct

        #  corners should be ordered in structs afterwards too
        ordered.append([])

        #  n is the corner that is currently checked
        n = open_corners[0]

        #  the first corner that is checked is alays at the front
        ordered[len(ordered) - 1].append(n)
        open_corners.pop(0)

        #  as long as there are corners to be sorted
        while len(open_corners) > 0:

            # print("len of open_corners: " + str(len(open_corners)))
            # print("len of boundaries: " + str(len(n.connections)))

            #  loop over the wall_connections and get the connection that is not already ordered
            for boundary_connection in n.connections:
                #  there should always be at least one boundary_connection in open_corners
                if boundary_connection in open_corners:
                    # print(boundary_connection)
                    #  the boundary_connection in open_corners is the new corner that has to be checked.
                    n = boundary_connection

            #  add last add boundary to ordered and remove it from open_corners
            open_corners.remove(n)
            ordered[len(ordered) - 1].append(n)

            # print("open corners (after remove(n)): " + str(open_corners))

    return ordered

#################
#    MapData    #
#################

class MapData:
    def __init__(self, img_dirs, fd_dir):
        """A Object to collect all other map objects and also convert them to string or to a picture"""
        print("Creating MapData-Object...")

        self.map_objects = []
        self.map_objects_nodes = []

        self.collectibles = []

        self.img_arrs = []  # collection of ImageArray objects

        """ In this loop the img_arrays are created and converted """
        for _dir in img_dirs:
            print("Creating ImageArray-Object for %s" % _dir)

            self.img_arrs.append(FieldObject(_dir))
            world_index = len(self.img_arrs) - 1
            img_arr = self.img_arrs[world_index]

            print("\tConverting ImageArray...")

            img_arr.expand_all(1, int(detail * 24))  # standard 16
            img_arr.expand_all(2, int(detail * 24))
            img_arr.expand_all(3, int(detail * 4))
            img_arr.expand_all(5, int(detail * 2))

            temp_map_objects = [
                [],  # walls
                [],  # traps
                [],  # swamps
                [],  # deposits
                []  # waters
            ]

            temp_map_objects_nodes = [
                [],  # walls
                [],  # traps
            ]

            for j in range(img_arr.height):
                for i in range(img_arr.width):
                    p = img_arr.img_arr[j][i]
                    if p.val <= 0:
                        pass
                    else:
                        temp_map_objects[p.val - 1].append(img_arr.flood_fill(i, j, p.val, -p.val, store=True))

            #  activate all pixels again
            for j in range(img_arr.height):
                for i in range(img_arr.width):
                    img_arr.img_arr[j][i].val = abs(img_arr.img_arr[j][i].val)

            print("\tCreating Polygons...")

            for i in range(0, len(temp_map_objects)):
                if i != 3:
                    object_type = temp_map_objects[i]
                    corners = []
                    for object_piece in object_type:
                        corners.append(get_corners(img_arr, object_piece))
                    temp_map_objects[i] = order_corners(corners)
                else:
                    temp_map_objects[3] = [img_arr.avg(deposit) for deposit in temp_map_objects[3]]

            for i in [0, 1]:
                for obj in temp_map_objects[i]:
                    for c in obj:
                        if c.match[1].field:
                            temp_map_objects_nodes[i].append(c.match[1])

            self.map_objects.append(temp_map_objects)
            self.map_objects_nodes.append(temp_map_objects_nodes)

            print("\tfinished")

        if path.isfile(fd_dir + "/Field.FD"):
            tree = ET.parse(fd_dir + "/Field.FD")
            root = tree.getroot()
            w1 = root.find("World1")
            w2 = root.find("World2")
            self.collectibles.append(find_color_points(w1, self.img_arrs[0]))
            self.collectibles.append(find_color_points(w2, self.img_arrs[1]))

            # print(self.collectibles)
        else:
            print("File: " + fd_dir + "/Field.FD not found")

    def show(self, scale):

        x_off = scale
        y_off = scale

        i = 0
        for img_arr in self.img_arrs:
            im = Image.new('HSV', ((img_arr.width + 2) * scale, (img_arr.height + 2) * scale))
            draw = ImageDraw.Draw(im)

            # show the walls
            draw_polygons(draw, self.map_objects[i][0], scale, sat=0, lum=255)

            # show the traps
            draw_polygons(draw, self.map_objects[i][1], scale, starting_hue=40, hue_dif=0)

            # show the swamps
            draw_polygons(draw, self.map_objects[i][2], scale, sat=0, lum=50)

            # show the bonus_areas
            draw_polygons(draw, self.map_objects[i][4], scale, starting_hue=80, hue_dif=0)

            im.show("Map%s" % i)
            im = im.convert(mode="RGB")
            im.save("debugging/Map%s.png" % i, "png")
            i += 1

    def __str__(self):
        file_content = ""
        i = 0
        for img_arr in self.img_arrs:
            wall_str = "const std::vector<Area>GAME%sWALLS = " % i  # {{{x1, y1}, {x2, y2}...}, {...}} (Polygon)
            trap_str = "const std::vector<Area>GAME%sTRAPS = " % i  # {{{x1, y1}, {x2, y2}...}, {...}} (Polygon)
            swamp_str = "const std::vector<Area>GAME%sSWAMPS = " % i  # {{x1, y1}, {x2, y2}...} (Polygon)
            bonus_area_str = "const std::vector<Area>GAME%sWATERS = " % i  # {{x1, y1}, {x2, y2}...} (Polygon)
            deposit_area_str = "const std::vector<PVector>GAME%sDEPOSITS = " % i  # {{x1, y1}, {x2, y2}...} (Single points)
            wall_nodes_str = "const std::vector<PVector>GAME%sWALLNODES = " % i  # {{x1, y1}, {x2, y2}...} (Single points)
            trap_nodes_str = "const std::vector<PVector>GAME%sTRAPNODES = " % i  # {{x1, y1}, {x2, y2}...} (Single points)
            collectibles_str = "const std::vector<Collectible> GAME%sCOLLECTIBLES = " % i

            wall_str += convert_arr_to_area_vector_string(self.map_objects[i][0])
            trap_str += convert_arr_to_area_vector_string(self.map_objects[i][1])
            swamp_str += convert_arr_to_area_vector_string(self.map_objects[i][2])
            deposit_area_str += str(self.map_objects[i][3]).replace("[", "{").replace("]", "}").replace(" ", "") + ";"
            bonus_area_str += convert_arr_to_area_vector_string(self.map_objects[i][4])

            wall_nodes_str += str(self.map_objects_nodes[i][0]).replace("[", "{").replace("]", "}").replace(" ",
                                                                                "") + ";"
            trap_nodes_str += str(self.map_objects_nodes[i][1]).replace("[", "{").replace("]", "}").replace(" ",
                                                                                "") + ";"

            collectibles_str += str(self.collectibles[i]).replace("[", "{").replace("]", "}").replace(" ", "") + ";"

            # TODO -- ONLY TEMP FOR DEBUGGING -- REMOVELATER

            file_content += "//------------- Game%s_Objects --------------//\n\n" % i

            file_content += "\t\t/*walls*/\n" + wall_str + \
                            "\n\t\t/*traps*/\n" + trap_str + \
                            "\n\t\t/*swamps*/\n" + swamp_str + \
                            "\n\t\t/*Water*/\n" + bonus_area_str + \
                            "\n\t\t/*deposit*/\n" + deposit_area_str

            file_content += "\n\n\t//------ Nodes ------//\n"

            file_content += "\t\t/*wall_nodes*/\n" + wall_nodes_str + \
                            "\n\t\t/*trap_nodes*/\n" + trap_nodes_str + "\n\n"

            file_content += "\n\n\t//------ Collectibles ------//\n"

            file_content += "\t\t/*collectibles*/\n" + collectibles_str + "\n\n"

            i += 1

        return file_content

def main():
    print("setting up...")

    # mapData = MapData(img_dirs=["debugging"], fd_dirs=FieldFD)
    mapData = MapData(img_dirs=[FieldA, FieldB], fd_dir=FieldFD)
    # mapData.show(10)

    mapData.show(10)

    begin = "\n\n\n" \
            "///   _______                _____          __\n" \
            "///  |   |   |.---.-..-----.|     \ .---.-.|  |_ .---.-.\n" \
            "///  |       ||  _  ||  _  ||  --  ||  _  ||   _||  _  |\n" \
            "///  |__|_|__||___._||   __||_____/ |___._||____||___._|\n" \
            "///                  |__|\n"

    f = open("../code/MapData.cpp")
    content = f.read()
    f.close()
    content_data = content.split(begin)
    if len(content_data) == 2:
        content_data[1] = begin + "\n\n\n" + str(mapData)
    else:
        content_data.append(begin + "\n\n\n" + str(mapData))

    data = "".join(content_data)

    f = open("../code/MapData.cpp", "w")
    f.write(data)
    f.close()

if __name__ == '__main__':
    main()


#!/usr/bin/python3

import argparse
import sys
import re

# Check correct usage
parser = argparse.ArgumentParser(description="Check your Bitmask.")
parser.add_argument('input', metavar='input', type=str,
                    help='Bitwise data input.')
args = parser.parse_args()

data = {}

class bit36:
    'Class for 36 bit unsigned integers'
    def __init__(self, val):
        if not isinstance(val, int):
            raise TypeError("Must be passed integer value.")
        if val < 0 or val > 68719476735:
            raise ValueError('Value initiated is outside bounds of 36-bit int.')

        self.bits = [0] * 36  # Most significant to least significant
        for i, v in enumerate('{0:036b}'.format(val)):
            self.bits[i] = v

    def setbit(self, i, v):
        self.bits[i] = v

    def __int__(self):
        return int(''.join(self.bits), 2)

    def __radd__(self, other):
        return other + int(self)

    def __repr__(self):
        return str(int(self))

    def __copy__(self):
        return bit36(int(self))

def main():
    # Part 1
    parseInput(args.input, 1)
    print(findInit())

    # Part 2
    parseInput(args.input, 2)
    print(findInit())

    # Debug
    # printData()

# Parse the input file
def parseInput(inp, version):
    global data
    data = {}
    mask = ['X'] * 36
    try:
        data_fh = open(inp, 'r')
    except IOError:
        sys.exit("Unable to open input file: " + inp)

    for line in data_fh:
        match = re.match(r"^mem\[(\d+)\] = (\d+)$", line, re.I)
        if not match:
            mask = list(line.strip("\n").split(" = ")[1])
        else:
            mem = bit36(int(match.group(1)))
            val = bit36(int(match.group(2)))

            if version == 1:
                # Apply mask to val
                for i, v in enumerate(mask):
                    if v != 'X':
                        val.setbit(i, v)

                data[int(mem)] = val

            elif version == 2:
                dontcares = []

                # Apply mask to mem and set all 1's / find dontcares
                for i, v in enumerate(mask):
                    if v == '1':
                        mem.setbit(i, v)
                    elif v == 'X':
                        dontcares.append(i)

                # From dontcares identify list of affected memory
                options = getDCs(dontcares)

                # Add each address
                for opt in options:
                    bits = mem.bits.copy()

                    for i, v in enumerate(opt):
                        bits[dontcares[i]] = v

                    address = int(''.join(bits), 2)
                    data[address] = val

# Return list of lists of all don't care states
def getDCs(dcs):
    options = []

    total = 2 ** len(dcs)
    fmt = '{0:0'+str(len(dcs))+'b}'

    for i in range(total):
        options.append(list(fmt.format(i)))

    return options

# For each pass, identify its seat
def findInit():
    return sum(data.values())

def printData():
    for mem in data:
        print(f"{mem}: {data[mem]}")

if __name__ == "__main__":
    main()


#!/usr/bin/env python
import pika

credentials = pika.PlainCredentials('test1', '1234')
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', virtual_host='TestName'))
channel = connection.channel()

channel.queue_declare(queue='Rob')

def callback(ch, method, properties, body):
    print(" [x] Received %r" % body)

channel.basic_consume(callback,
                      queue='Rob',
                      no_ack=True)

print(' [*] Waiting for messages. To exit press CTRL+C')
channel.start_consuming()


from __future__ import annotations

import torch
import torch.nn as nn

class ConvolutionBlock(nn.Module):
    """
    Double convolution block, changeing the number of channels.
    """

    def __init__(self: ConvolutionBlock, in_channels: int, out_channels: int) -> None:
        super().__init__()

        self.conv = nn.Sequential(
            nn.Conv2d(in_channels=in_channels,
                      out_channels=out_channels,
                      kernel_size=3,
                      stride=1,
                      padding='same',
                      bias=False),
            nn.BatchNorm2d(num_features=out_channels),
            nn.LeakyReLU(inplace=True),
            nn.Conv2d(in_channels=out_channels,
                      out_channels=out_channels,
                      kernel_size=3,
                      stride=1,
                      padding='same',
                      bias=False),
            nn.BatchNorm2d(num_features=out_channels),
            nn.LeakyReLU(inplace=True)
        )

    def forward(self: ConvolutionBlock, x: torch.Tensor) -> torch.Tensor:
        return self.conv(x)

class EncoderBlock(nn.Module):
    """
    Encoder block. Convolution and downsampling.
    """

    def __init__(self: EncoderBlock, in_channels: int, out_channels: int) -> None:
        super().__init__()

        self.conv = ConvolutionBlock(
            in_channels=in_channels, out_channels=out_channels)
        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)

    def forward(self: EncoderBlock, x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
        skip = self.conv(x)
        x = self.pool(skip)

        return skip, x

class DecoderBlock(nn.Module):
    """
    Decoder block. Upsampling and convolution. Takes skip connections from
    the encoder path.
    """

    def __init__(self: DecoderBlock, in_channels: int, out_channels: int) -> None:
        super().__init__()

        self.upsample = nn.ConvTranspose2d(
            in_channels=in_channels, out_channels=out_channels, kernel_size=2, stride=2)
        self.conv = ConvolutionBlock(in_channels=in_channels,
                                     out_channels=out_channels)

    def forward(self: DecoderBlock, x: torch.Tensor, skip: torch.Tensor) -> torch.Tensor:
        x = self.upsample(x)
        x = torch.cat((x, skip), dim=1)

        return self.conv(x)

class BasicUNet(nn.Module):
    """
    A basic U-Net. The prediction has a tensor with one channel,
    but the same height and with as for the input.
    """

    def __init__(self: BasicUNet, in_channels: int) -> None:
        super().__init__()

        filters = [16, 32, 64, 128, 256]

        self.encoders = nn.ModuleList()
        num_in = in_channels
        for num_out in filters[:-1]:
            self.encoders.append(EncoderBlock(num_in, num_out))
            num_in = num_out

        self.bottleneck = ConvolutionBlock(filters[-2], filters[-1])

        self.decoders = nn.ModuleList()
        num_in = filters[-1]
        for num_out in reversed(filters[:-1]):
            self.decoders.append(DecoderBlock(num_in, num_out))
            num_in = num_out

        self.final_conv = nn.Sequential(
            nn.Conv2d(
                in_channels=filters[0], out_channels=1, kernel_size=1, stride=1),
            nn.ReLU()
        )

    def forward(self: BasicUNet, x: torch.Tensor) -> torch.Tensor:
        skip_connections = list()
        for encoder in self.encoders:
            skip, x = encoder(x)
            skip_connections.append(skip)

        x = self.bottleneck(x)

        for decoder, skip in zip(self.decoders, reversed(skip_connections)):
            x = decoder(x, skip)

        return self.final_conv(x)


from __future__ import print_function # Για τις όμορφες ιδιότητες της python 3!
from datetime import datetime
import datetime as dt # Για να μην μπλέξουν οι βιβλιοθήκες!

#Τίτλοι ημερών
week = ['Su ', 'Mo ', 'Tu ', 'We ', 'Th ', 'Fr ', 'Sa ']

# Οι μήνες και το εύρος ημερών τους
# + 1 για αποφυγή μπερδέματος με το max των ημερών
calender = [('January', range(1, 31 + 1)),
            ('Feburary', range(1, 28 + 1)),
            ('March', range(1, 31 + 1)),
            ('April', range(1, 30 + 1)),
            ('May', range(1, 31 + 1)),
            ('June', range(1, 30 + 1)),
            ('July', range(1, 31 + 1)),
            ('August', range(1, 31 + 1)),
            ('September', range(1, 30 + 1)),
            ('October', range(1, 31 + 1)),
            ('November', range(1, 30 + 1)),
            ('December', range(1, 31 + 1))]

#Ελεγχος εγκυροτητας μηνα
while True:
	try:
		month = int(raw_input("Δώσε τον μήνα που επιθυμείς σε αριθμητική μορφή (1-12): "))
		if month <= 0 or month > 12:
			print ("\n")
			print ("Μη έγκυρος μήνας!")
			print ("\n")
			continue
		else:
			break
	except Exception:
		print ("\n")
		print ("Μη έγκυρος μήνας!")
		print ("\n")

print ("\n")

while True:
	try:
		yeard = int(raw_input("Δώσε τον χρόνο που επιθυμείς σε αριθμητική μορφή: "))
		if yeard <=0 or yeard >= 10000:
			print ("\n")
			print ("Ο χρόνος πρέπει να είναι έγκυρος και σε αριθμητική μορφή.")
			print ("\n")
			continue
		else:
			break
	except Exception:
		print ("\n")
		print ("Ο χρόνος πρέπει να είναι έγκυρος και σε αριθμητική μορφή.")
		print ("\n")

print ("\n")
# Για να εμφανιστεί έπειτα ως τίτλος
if month ==1:
	month2 = "January"
if month ==2:
	month2 = "Feburary"
if month ==3:
	month2 = "March"
if month ==4:
	month2 = "April"
if month ==5:
	month2 = "May"
if month ==6:
	month2 = "June"
if month ==7:
	month2 = "July"
if month ==8:
	month2 = "August"
if month ==9:
	month2 = "September"
if month ==10:
	month2 = "October"
if month ==11:
	month2 = "November"
if month ==12:
	month2 = "December"

#Πότε αρχίζει ο μήνας
start = datetime(yeard, month,1).isoweekday()
if start == 7:
	startw = week[0]
if start == 1:
	startw = week[1]
if start == 2:
	startw = week[2]
if start == 3:
	startw = week[3]
if start == 4:
	startw = week[4]
if start == 5:
	startw = week[5]
if start == 6:
	startw = week[6]

#Πότε τελειώνει ο μήνας
def last_day_of_month(date):
    next_month = date.replace(day=28) + dt.timedelta(days=4)
    return next_month - dt.timedelta(days=next_month.day)

endd = last_day_of_month(dt.date(yeard, month, 1))
# Παίρνω την ημέρα μόνο
endd = endd.day

#Εμφάνιση ημερολογίου
def calendar(year, start_day,month2):
    # Ανάλογα με την μέρα έναρξης του μήνα ,αρχικοποιώ το starting position στο calendar
		start_pos = week.index(start_day)

    # αν είναι True, αλλάζω τον Φεβρουάριο σε 29 days
		if leap(year):
			calender[1] = ('Feburary', range(1, 29 + 1))

        # Print τον τίτλο του μήνα
		print('{0} {1}'.format(month2, year).center(20, ' '))
        # Print τούς τίτλους των ημερών
		print(''.join(['{0:<3}'.format(w) for w in week]))
        # Βάζω κενά αν η ημέρα δεν αρχίζει από Sunday
		print('{0:<3}'.format('')*start_pos,end='')

		for day in range(1,endd+1):
            # Print την ανάλογη ημέρα
				print('{0:>2} '.format(day),end='')
				start_pos += 1
				if start_pos == 7:
                # Αν start_pos == 7 (Saturday) αλλάζω γραμμή
					print()
					start_pos = 0 # Reset μετρητή
		print('\n')

def leap(year):
    #Check αν year ειναι  leap year (δίσεκτος)
    if year % 4 == 0:
        if year % 100 == 0:
            if year % 400 == 0:
                return True
            else:
                return False
        else:
            return True
    else:
        return False
# Καλώ την συνάρτηση και εμφανίζω το ημερολόγιο.
calendar(yeard,startw,month2)


from server.vitamin import models
from fastapi import APIRouter, HTTPException, Depends
from typing import List

from . import schemas, crud
from sqlalchemy import func
from sqlalchemy.orm import Session
from ..dependencies import get_db
from datetime import date

router = APIRouter(
    prefix="/vitamins",
    tags=["vitamins"],
    responses={404: {"description": "Not found"}},
)

## Vitamins
### Vitamin Base
@router.post("/", response_model=schemas.Vitamin)
def create_vitamin(vit: schemas.VitaminCreate, db: Session = Depends(get_db)):
    return crud.create_vitamin(db=db, vit=vit)

@router.get("/", response_model=List[schemas.Vitamin])
def read_vitamins(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    vitamins = crud.get_vitamins(db, skip=skip, limit=limit)
    return vitamins

@router.get("/summary/{cur_date}", response_model=List[schemas.VitaminSummary])
def read_summary(cur_date: date, skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    cgoal = db.query(models.Vitamin_Goal,
                    func.rank().over(
                        order_by=models.Vitamin_Goal.date.desc(),
                        partition_by=models.Vitamin_Goal.vitamin_id
                    ).label('rnk')
                    ).subquery()

    summ = db.query(models.Vitamin, func.sum(models.VitaminRecord.qty).label('totals'))\
        .join(models.VitaminRecord).filter(models.VitaminRecord.date == cur_date)\
        .group_by(models.Vitamin.id).subquery()

    res = db.query(models.Vitamin.id, models.Vitamin.name, summ.c.totals, cgoal.c.qty)\
        .outerjoin(summ, models.Vitamin.id == summ.c.id)\
        .outerjoin(cgoal, models.Vitamin.id == cgoal.c.vitamin_id).\
        filter(cgoal.c.rnk == 1).distinct()

    results=[]
    for r in res:
        payload={"vitamin_id": r.id,
        "name":r.name,
        "goal":r.qty,
        "amount":r.totals}
        results.append(payload)
    return results

@router.get("/{vit_id}", response_model=schemas.Vitamin)
def read_vitamin(vit_id: int, db: Session = Depends(get_db)):
    db_vitamin = crud.get_vitamin(db, vit_id=vit_id)
    if db_vitamin is None:
        raise HTTPException(status_code=404, detail="Vitamin record not found")
    return db_vitamin

@router.delete("/{vit_id}", status_code=204)
def delete_vitamin(vit_id: int, db: Session = Depends(get_db)):
    db_vitamin = crud.get_vitamin(db, vit_id=vit_id)
    if db_vitamin is None:
        raise HTTPException(status_code=404, detail="Water_record not found")
    crud.delete_vitamin(db, vit_id=vit_id)

### Vitamin Records

@router.post("/record/", response_model=schemas.VitaminRecord)
def create_vitamin_record(
        vit_id: int, vit_rec: schemas.VitaminRecordCreate, db: Session = Depends(get_db)):
    return crud.create_vitamin_record(db=db, vit_rec=vit_rec, vit_id=vit_id)

@router.get("/record/", response_model=List[schemas.VitaminRecord])
def read_vitamin_records(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    vitamin_records = crud.get_vitamin_records(db, skip=skip, limit=limit)
    return vitamin_records

@router.get("/record/{vit_rec_id}", response_model=schemas.VitaminRecord)
def read_vit_record(vit_rec_id: int, db: Session = Depends(get_db)):
    db_vit_record = crud.get_vitamin_record(db, vit_rec_id=vit_rec_id)
    if db_vit_record is None:
        raise HTTPException(status_code=404, detail="Vitamin record not found")
    return db_vit_record

@router.delete("/record/{vit_rec_id}", status_code=204)
def delete_vit_record(vit_rec_id: int, db: Session = Depends(get_db)):
    db_vit_rec = crud.get_vitamin_record(db, vit_rec_id=vit_rec_id)
    if db_vit_rec is None:
        raise HTTPException(status_code=404, detail="Vitamin Record not found")
    crud.delete_vitamin_record(db, vit_rec_id=vit_rec_id)

@router.get("/goal/", response_model=List[schemas.VitaminGoal])
def read_vitamin_goals(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
    vitamin_goals = crud.get_vitamin_goals(db, skip=skip, limit=limit)
    return vitamin_goals

@router.post("/goal/", response_model=schemas.VitaminGoal)
def create_vitamin_record(
        vit_id: int, vit_goal: schemas.VitaminGoalCreate, db: Session = Depends(get_db)):
    return crud.create_vitamin_goal(db=db, vit_goal=vit_goal, vit_id=vit_id)

@router.get("/goal/first/{vit_id}", response_model=schemas.VitaminGoal)
def read_current_vitamin_goal(vit_id: int, db: Session = Depends(get_db)):
    db_vit_goal = crud.get_current_vit_goal(db, vit_id=vit_id)
    if db_vit_goal is None:
        raise HTTPException(status_code=404, detail="Vitamin Record not found")
    return db_vit_goal


#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

import logging
from copy import copy
from functools import partial
from itertools import chain, combinations, product

import six

from .patterns import dollar_prefix, pop_dollar_prefix, _convert_names_with_underlines_to_dots

logger = logging.getLogger(__name__)

class MongoAggregation(list):

    def __init__(self, pipeline='', collection='', allowDiskUse=False):
        self.collection = collection
        self.allowDiskUse = allowDiskUse
        self.actual_fields = set()
        self.pipeline = pipeline if pipeline else []

    def aggregate(self, collection='', allowDiskUse=False, as_list=False, collation=None):
        if collection:
            self.collection = collection
        if self.collection.__class__.__name__ == 'TopLevelDocumentMetaclass':
            self.collection = self.collection.objects
        if allowDiskUse:
            self.allowDiskUse = allowDiskUse
        if self.collection.__class__.__name__ != 'QuerySet' and not self.collection:
            logger.error('Агрегация невозможна: не указана коллекция')
            return
        aggregate = partial(self.collection.aggregate, allowDiskUse=self.allowDiskUse, collation=collation)
        if self.collection.__class__.__name__ == 'QuerySet':
            result = aggregate(*self.pipeline)
        else:
            result = aggregate(self.pipeline)
        return list(result) if as_list else result

    def append(self, object=None, *args):
        if not object: object = []
        if isinstance(object, list):
            self.pipeline.extend(object)
        else:
            self.pipeline.append(object)
        if args:
            for arg in args:
                self.pipeline.append(arg)
        return self

    def extend(self, object=None, *args):
        self.append(object, *args)

    def count(self, **kwargs):
        self.pipeline.append({'$count': 'count'})
        count = next(self.aggregate(**kwargs), {}).get('count', 0)
        # Revert last stage
        self.revert_last_stage()
        return count

    def match(self, *args, **kwargs):
        if not args and not kwargs:
            return self
        if args:
            for arg in args:
                self.pipeline.append({
                    '$match': arg
                })
        kwargs = _convert_names_with_underlines_to_dots(kwargs, convert_operators=True)
        if args and kwargs:
            self.pipeline[-1]['$match'].update(kwargs)
        elif kwargs:
            self.pipeline.append({
                '$match': kwargs
            })
        self._add_to_actual_fields([
            x for x in self.last_stage.statement.keys()
            if not x.startswith('$')
        ])
        return self

    def lookup_unwind(self, collection, local_field='_id', as_field='', foreign_field='_id',
                      preserveNullAndEmptyArrays=True):
        if not as_field:
            as_field = local_field
        self.lookup(collection, local_field, as_field, foreign_field),
        self.unwind(as_field, preserveNullAndEmptyArrays)
        return self

    def lookup(self, collection, local_field='_id', as_field='', foreign_field='_id'):
        if not as_field:
            as_field = local_field
        self.pipeline.append({"$lookup": {
            'from': collection,
            'foreignField': foreign_field,
            'localField': local_field,
            'as': as_field,
        }})
        self._add_to_actual_fields(local_field, ignore_if_theres_children=True)
        self._add_to_actual_fields(as_field, ignore_if_theres_children=True)
        return self

    def unwind(self, field, preserveNullAndEmptyArrays=False):
        field = dollar_prefix(field)
        if preserveNullAndEmptyArrays:
            self.pipeline.append({'$unwind': {'path': field, 'preserveNullAndEmptyArrays': True}})
        else:
            self.pipeline.append({'$unwind': field})
        self._add_to_actual_fields(field, ignore_if_theres_children=True)
        return self

    def order_by(self, *args, **kwargs):
        return self.sort(*args, **kwargs)

    def sort(self, *args, **kwargs):
        def _prepare_str_order_rule(order_rule):
            if not isinstance(order_rule, six.string_types):
                return order_rule
            sign = -1 if order_rule.startswith('-') else 1
            sort_field = order_rule.strip('-+')
            return {sort_field: sign}

        kwargs = _convert_names_with_underlines_to_dots(kwargs)
        # Складываем все в args
        args = list(args)
        if kwargs:
            args.append(kwargs)

        if not args: return self
        # Делаем project
        for order_rule in args:
            order_rule = _prepare_str_order_rule(order_rule)
            self.pipeline.append({"$sort": order_rule})
            self._add_to_actual_fields(order_rule.keys())
        return self

    def skip(self, offset=0):
        if not offset: return self
        self.pipeline.append({"$skip": offset})
        return self

    def limit(self, limit=0):
        if not limit: return self
        self.pipeline.append({"$limit": limit})
        return self

    def project(self, *args, **kwargs):
        kwargs = _convert_names_with_underlines_to_dots(kwargs)
        # Складываем все в args
        if args and kwargs:
            args[-1].update(kwargs)
        elif kwargs:
            args = (kwargs,)
        if not args: return self

        # Делаем project
        for arg in args:
            self.pipeline.append({'$project': arg})
        # Заполняем актуальные поля
        self._set_actual_fields(arg.keys())
        return self

    def _get_fields_without_children(self, fields):
        parents = copy(fields)
        for one, another in combinations(fields, 2):
            is_children = another.startswith(f'{one}.')
            if is_children:
                parents.discard(another)
                continue
            is_children = one.startswith(f'{another}.')
            if is_children:
                parents.discard(one)
                continue
        return parents

    @staticmethod
    def _str_to_list(variable):
        return variable.split(',') if isinstance(variable, str) else variable

    def _set_actual_fields(self, *fields):
        """Устанавливает список актуальных полей"""
        self.actual_fields = set(chain(*fields))

    def _add_to_actual_fields(self, fields, ignore_if_theres_children=True):
        """Добавляет поля в список актуальных полей"""
        _fields = set(self._str_to_list(fields))
        if ignore_if_theres_children:
            _fields = [
                field
                for field in _fields
                if pop_dollar_prefix(field) not in self.get_parents(self.actual_fields, all_levels=True)
            ]
        for field in _fields:
            self.actual_fields.add(pop_dollar_prefix(field))

    @staticmethod
    def get_parents(fields, all_levels=False, upper_level=True):
        if isinstance(fields, str):
            fields = set(fields.split(','))

        # Самый нижний уровень
        if not all_levels and not upper_level:
            return {field.split('.')[0] for field in fields}

        # Все уровни и верхний
        parents = set()
        for field in fields:
            splitted = field.split('.')
            for i in range(len(splitted)):
                splitted.pop()
                if not splitted: break
                parents.add('.'.join(splitted))
                # Только верхний уровень
                if not all_levels: break
        return parents

    def replace_root(self, expression):
        """Replaces the input document with the specified document.
        The operation replaces all existing fields in the input document, including the _id field.
        From MongoDB version 3.4."""
        if isinstance(expression, six.string_types):
            expression = dollar_prefix(expression)
        self.pipeline.append({'$replaceRoot': {'newRoot': expression}})
        return self

    def add_fields(self, **kwargs):
        """Adds new fields to documents.
        $addFields outputs documents that contain all existing fields from the input documents and newly added fields.
        From MongoDB version 3.4
        Starting in version 4.2, MongoDB adds a new aggregation pipeline stage $set that is an alias for $addFields.
        """
        kwargs = _convert_names_with_underlines_to_dots(kwargs)
        self.pipeline.append({'$addFields': kwargs})
        self._add_to_actual_fields(kwargs.keys())
        return self

    def set(self, **kwargs):
        """Adds new fields to documents.
        $set outputs documents that contain all existing fields from the input documents and newly added fields.
        From MongoDB version 4.2."""
        kwargs = _convert_names_with_underlines_to_dots(kwargs)
        self.pipeline.append({'$set': kwargs})
        self._add_to_actual_fields(kwargs.keys())
        return self

    def smart_project(self, include_fields='', exclude_fields='', include_all_by_default=True, *args, **kwargs):
        """Custom realization of $addFields for an older versions of MongoDB. Bases on $project stage."""
        if not include_all_by_default:
            self.actual_fields = set()

        if include_fields:
            include_fields = set(include_fields.split(','))
            parent_fields = self.get_parents(include_fields, True)
            self.actual_fields -= parent_fields
            self.actual_fields.update(include_fields)

        # Проверяем обращение к полям объекта с помощью __ в kwargs
        kwargs = _convert_names_with_underlines_to_dots(kwargs)

        # Складываем kwargs в args
        args = self._add_kwargs_to_args(args, kwargs)

        if exclude_fields:
            exclude_fields = set(exclude_fields.split(','))

        if any(args) or include_fields:
            # Exclude fields may contain some information of what fields to include (calculate by contradiction)
            if exclude_fields:
                # Add parents first
                # actual_fields = {'count'}
                # exclude_field = 'menu.elements.option'
                # Parent: 'menu.elements'
                # Result: actual_fields = {'count', 'menu.elements'}
                for excl_field in exclude_fields:
                    self.actual_fields.update(self.get_parents(excl_field, True))
                for excl_field in exclude_fields:
                    # Remove the field itself
                    self.actual_fields.discard(excl_field)
                    # Remove all children
                    # actual_fields = {'menu.elements.option', 'menu.elements.count', 'count'}
                    # exclude_field = 'menu.elements'
                    # Children: 'menu.elements.option', 'menu.elements.count'
                    # Result: actual_fields = {'count'}
                    child_fields = {
                        act_field for act_field in self.actual_fields
                        if '{}.'.format(excl_field) in act_field
                    }
                    self.actual_fields -= child_fields

            paths = self._get_stage_hierarchy_fields(args[-1])
            if not paths and self.actual_fields:
                prolong_fields = self.actual_fields
            else:
                prolong_fields = set([actual_field
                                      for new_field, actual_field in product(paths, self.actual_fields)
                                      if actual_field != new_field
                                      and not actual_field.startswith(f'{new_field}.')
                                      and not new_field.startswith(f'{actual_field}.')])
            prolong_fields = self._get_fields_without_children(prolong_fields)
            for field in prolong_fields:
                args[-1].setdefault(field, 1)

            # Делаем project
            if args:
                for arg in args:
                    self.pipeline.append({'$project': arg})
                # Заполняем актуальные поля
                self._set_actual_fields(self._get_stage_hierarchy_fields(arg))

        # Then esclude fields and children
        if exclude_fields:
            # !!! This is the same section, we double it because it may save some fields in earlier projections
            # Add parents first
            for excl_field in exclude_fields:
                self.actual_fields.update(self.get_parents(excl_field, True))
            for excl_field in exclude_fields:
                # Remove the field itself
                self.actual_fields.discard(excl_field)
                # Remove all children
                child_fields = {
                    act_field for act_field in self.actual_fields
                    if '{}.'.format(excl_field) in act_field
                }
                self.actual_fields -= child_fields
            # Exclude projection itself
            self.pipeline.append({'$project': {field: 0 for field in exclude_fields}})

        return self

    def _get_stage_hierarchy_fields(self, stage, prefix=''):
        def get_fields_for_dict_value(value, prefix=''):
            if not isinstance(value, dict):
                return []
            if list(value.keys())[0].startswith('$'):
                literal = value.get('$literal')
                if literal and isinstance(literal, dict):
                    return self._get_stage_hierarchy_fields(literal, prefix)

                return [prefix]
            return self._get_stage_hierarchy_fields(value, prefix)

        paths = []
        for field, value in stage.items():
            new_prefix = f'{prefix}.{field}' if prefix else field
            if isinstance(value, list):
                for obj in value:
                    if isinstance(obj, dict):
                        paths.extend(get_fields_for_dict_value(obj, prefix=new_prefix))
                        break
                continue
            elif isinstance(value, dict):
                paths.extend(get_fields_for_dict_value(value, prefix=new_prefix))
                continue
            paths.append(new_prefix)
            continue
        return paths

    @staticmethod
    def _add_kwargs_to_args(args, kwargs):
        # Складываем kwargs в args
        if args and kwargs:
            args[-1].update(kwargs)
        elif kwargs:
            args = (kwargs,)
        elif not args:
            args = ({},)
        return args

    def group(self, *args, **kwargs):
        """
        Стадия группировки

        :param group_by: Поля группировки. Можно подавать строкой через запятую, либо списком, либо словарем
        :param first_fields: Список полей для оператора $first. Строка либо список.
        :param min_fields: Список полей для оператора $min. Строка либо список.
        :param max_fields: Список полей для оператора $max. Строка либо список.
        :param sum_fields: Список полей для оператора $sum. Строка либо список.
        :param counter_fields: Список полей для оператора $sum, количество просуммированных строк. Строка либо список.
        :param avg_fields: Список полей для оператора $avg. Строка либо список.
        :param push_fields: Список полей для оператора $push. Строка либо список.
        :param addToSet_fields: Список полей для оператора $addToSet. Строка либо список.
        :param operator_by_default: Используемый оператор для остальных полей actual_fields. По умолчанию не добавляет остальные поля в результат.
        :param exclude_fields: Использовать operator_by_default для всех, кроме перечисленных. Строка через запятую, либо список.
        :param args: Описание остальных полей группировки словарем, либо несколькими словарями.
        :param kwargs: Описание остальных полей группировки именованными аргументами.
        :return:
        """
        group_by = kwargs.pop('group_by', None)
        first_fields = kwargs.pop('first_fields', None)
        min_fields = kwargs.pop('min_fields', None)
        max_fields = kwargs.pop('max_fields', None)
        sum_fields = kwargs.pop('sum_fields', None)
        counter_fields = kwargs.pop('counter_fields', None)
        avg_fields = kwargs.pop('avg_fields', None)
        push_fields = kwargs.pop('push_fields', None)
        add_to_set_fields = kwargs.pop('addToSet_fields', None)
        operator_by_default = kwargs.pop('operator_by_default', None)
        exclude_fields = kwargs.pop('exclude_fields', False)
        if exclude_fields:
            exclude_fields = exclude_fields.split(',')

        # Инициализируем stage группировки
        stage = {}

        kwargs = _convert_names_with_underlines_to_dots(kwargs, convert_operators=True)
        # Добавляем kwargs и args в stage
        args = self._add_kwargs_to_args(args, kwargs)
        for arg in args:
            if not isinstance(arg, dict): continue
            if not arg: continue
            stage.update(arg)

        # Подготовка и обработка операторов группировки
        operators = {
            '$first': first_fields,
            '$min': min_fields,
            '$max': max_fields,
            '$sum': sum_fields,
            '$avg': avg_fields,
            '$push': push_fields,
            '$addToSet': add_to_set_fields,
        }
        for operator, fields in operators.items():
            if not fields: continue
            self._group_set_operator(stage, fields, operator)
        if counter_fields:
            self._counter_set_operator(stage, counter_fields)

        # Указываем поле группировки
        stage['_id'] = self._group_get_id(group_by)

        # Устанавливаем по дефолту $first для остальных полей
        if operator_by_default:
            parent_fields = self.get_parents(self.actual_fields, True)
            if isinstance(stage['_id'], str):
                _id_fields = pop_dollar_prefix(stage['_id'])
            else:
                _id_fields = stage['_id'].keys()
            for field in parent_fields:
                if field in chain(stage, _id_fields):
                    continue
                elif field in exclude_fields:
                    continue
                stage[field] = {operator_by_default: dollar_prefix(field)}

        # Формируем actual_fields
        if not stage['_id']:
            _id_fields = []
        elif isinstance(stage['_id'], str):
            _id_fields = ['_id']
        else:
            _id_fields = ['_id.{}'.format(key) for key in stage['_id'].keys()]

        def get_prolonged_actual_fields_for(*fields_lists):
            prolong_fields = []
            new_fields = [self._str_to_list(fields) for fields in fields_lists if fields]
            for actual_field, new_field in product(self.actual_fields, chain(*new_fields)):
                if not actual_field.startswith(new_field):
                    continue
                prolong_fields.append(actual_field)
            return prolong_fields

        prolonged_actual_fields = get_prolonged_actual_fields_for(first_fields, push_fields, add_to_set_fields)
        self._set_actual_fields(stage, _id_fields, prolonged_actual_fields)

        self.pipeline.append({'$group': stage})
        return self

    def _group_set_operator(self, stage, fields, operator):
        if not fields: return
        if isinstance(fields, str):
            fields = fields.split(',')
        fields = _convert_names_with_underlines_to_dots(fields)
        if isinstance(fields, dict):
            fields = fields.keys()
        for field in fields:
            stage[pop_dollar_prefix(field)] = {operator: dollar_prefix(field)}

    def _counter_set_operator(self, stage, fields):
        if not fields: return
        if isinstance(fields, str):
            fields = fields.split(',')
        fields = _convert_names_with_underlines_to_dots(fields)
        if isinstance(fields, dict):
            fields = fields.keys()
        for field in fields:
            stage[pop_dollar_prefix(field)] = {'$sum': 1}

    def _group_get_id(self, group_by=None):
        # Допустимая ситуация: группировка всех строк в одну
        if not group_by: return None

        # Преобразуем к списку, если строка
        if isinstance(group_by, str):
            group_by = group_by.split(',')

        # Преобразуем имена вида item__name к item.name
        group_by = _convert_names_with_underlines_to_dots(group_by)

        # Если group_by - словарь, считаем его сформированным корректно
        if isinstance(group_by, dict):
            return group_by

        # Если список, формируем словарь или одно соответсвие
        if isinstance(group_by, list):
            if len(group_by) == 1:
                return dollar_prefix(group_by[0])

            return {pop_dollar_prefix(field): dollar_prefix(field) for field in group_by}

        # Опасная ситуация, но иначе никак - пусть группирует по всем строкам
        return None

    @property
    def last_stage(self):
        if not self.pipeline:
            return None
        return LastStage(self.pipeline)

    def revert_last_stage(self):
        if not self.pipeline:
            return
        self.pipeline = self.pipeline[:-1]

    def get_first(self, default=None, **kwargs):
        # Limit pipeline to 1 document
        self.limit(1)
        # Get first document to include statistics_today lately
        first_doc = next(self.aggregate(**kwargs), default)
        # Undo limit
        self.revert_last_stage()
        return first_doc

    def get_count(self, **kwargs):
        return self.count(**kwargs)

class LastStage():

    def __init__(self, pipeline):
        if not pipeline:
            raise Exception("Последнего этапа в этой агрегации не существует.")
        self.__data = pipeline[-1]

    @property
    def name(self):
        return list(self.__data.keys())[0]

    @name.setter
    def name(self, value):
        self.__data[value] = self.__data.pop(self.name)

    @property
    def statement(self):
        return self.__data[self.name]

    @statement.setter
    def statement(self, value):
        self.__data[self.name] = value

    def __repr__(self):
        return str(self.__data)

if __name__ == "__main__":
    import logging
    import os

    from dotenv import load_dotenv

    load_dotenv()

    mongo_connection = os.environ.get('MONGODB_HOST', 'mongodb://localhost:27017/test')
    import pymongo
    from datetime import datetime
    from dateutil.relativedelta import relativedelta

    logger = logging.getLogger()
    client = pymongo.MongoClient(mongo_connection)
    db = client.get_default_database()
    today = datetime.now()
    yesterday = today - relativedelta(days=1)

    pipeline = MongoAggregation(collection=db.action)
    pipeline.match(
        {'date': {'$gte': yesterday, '$lt': today}},
        completed=True,
        _cls={'$in': [
            'Action.Order', 'Action.StorageIncome', 'Action.StorageCancellation', 'Action.StorageMovementOutcome'
        ]},
    ).smart_project(
        'transactions.amount,_cls', '_id'
    ).append([
        {'$project': {
            '_cls': 1,
            'date': 1,
            'transactions.amount': 1,
            'transactions.cashbox': 1,
        }},
    ]).project(
        {'transactions.amount': 1, 'transactions.cashbox': 1}, _cls=1, date=1
    ).project(
        transactions=1, _cls=1, date=1
    )
    cursor = pipeline.aggregate()

    for doc in cursor:
        print(doc)
        break


__author__ = 'adonis'

from website.models import GalleryArticle
from django.shortcuts import get_object_or_404
from website.views.util import render, paginate

def gallery_index(request):
    article_list = GalleryArticle.objects.published(request.production).order_by('-pub_date')
    article_list = paginate(request, article_list)
    context = {
        'article_list': article_list,
        'url_namespace': 'gallery:article',
        'read_on_message': 'View the pictures...'
    }
    return render(request, 'website/gallery/index.html', context)

def gallery_article(request, article_id):
    article = get_object_or_404(GalleryArticle.objects.published(request.production), slug=article_id)
    context = {'article': article}
    return render(request, 'website/gallery/article.html', context)

def gallery_xml(request, article_id):
    response = get_object_or_404(GalleryArticle.objects.published(request.production), slug=article_id)
    context = {
        'article': response
    }
    return render(request, 'website/rss/gallery.xml', context)


import urllib2
from bs4 import BeautifulSoup
from multiprocessing import Pool

def crawl():
    applist = open('absent_google_play').readlines()
    apptag  = open('app_tag.wandoujia.csv', 'w')
    apptag.close()
    # multi-tasks
    n = 4
    p = Pool(n)
    for i in range(n):
        p.apply_async(subtask, args=(applist, n, i))
    p.close()
    p.join()

def subtask(applist, n, i):
    # query every n app starting from i-th app
    for app in applist[i::n]:
        app = app.strip()
        url = 'http://www.wandoujia.com/apps/%s' % app
        try:
            page = urllib2.urlopen(url, timeout=5).read()
            soup = BeautifulSoup(page)
            tags = soup(class_='tag-box')[0].text.encode('u8').strip().replace('\n', '_')
            apptag = open('app_tag.wandoujia.csv', 'a')
            print >> apptag, app + ',' + tags
            apptag.close()
        except:
            apptag = open('app_tag.wandoujia.csv', 'a')
            print >> apptag, app + ',' + 'except'
            apptag.close()

if __name__ == '__main__':
    crawl()