"""Take a dataset and produce two zarr collections chunked along the temporal and spatial axis respectively."""

import os
import argparse
import logging

import xarray as xr
from rechunker import rechunk
import dask.diagnostics
import zarr
import cmdline_provenance as cmdprov

dask.diagnostics.ProgressBar().register()
logging.basicConfig(level=logging.INFO)

def define_target_chunks(ds, var):
    """Create a target chunks dictionary."""

    if 'latitude' in ds[var].dims:
        chunks = {'time': len(ds['time']), 'latitude': 10, 'longitude': 10}
    else:
        chunks = {'time': len(ds['time']), 'lat': 10, 'lon': 10}
    target_chunks_dict = {var: chunks}
    variables = list(ds.keys())
    variables.remove(var)
    coords = list(ds.coords.keys())
    for name in coords + variables:
        target_chunks_dict[name] = None

    return target_chunks_dict

def main(args):
    """Run the command line program."""

    ds = xr.open_mfdataset(args.infiles)
    ds = ds.chunk({'time': 365})
    assert not os.path.isdir(args.temporal_chunked_collection), \
        f"You need to delete existing zarr collection: {args.temporal_chunked_collection}"
    ds.attrs['history'] = cmdprov.new_log(
        infile_logs={args.infiles[0]: ds.attrs['history']}
    )
    for var in ds.variables:
        ds[var].encoding = {}

    logging.info('Writing the temporal chunked collection...')
    ds.to_zarr(args.temporal_chunked_collection)
    zarr.consolidate_metadata(args.temporal_chunked_collection)

    source_group = zarr.open(args.temporal_chunked_collection)
    target_chunks_dict = define_target_chunks(ds, args.var)
    max_mem = '1GB'
    group_plan = rechunk(
        source_group,
        target_chunks_dict,
        max_mem,
        args.spatial_chunked_collection,
        temp_store=args.temp_collection
    )
    logging.info('Writing the spatial chunked collection...')
    group_plan.execute()
    zarr.consolidate_metadata(args.spatial_chunked_collection)

    clean_up_command = f'rm -r {args.temp_collection}'
    logging.info(clean_up_command)
    os.system(clean_up_command)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description=__doc__,
                                     formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("infiles", type=str, nargs='*', help="Input files")
    parser.add_argument("var", type=str, help="Variable")
    parser.add_argument("temporal_chunked_collection", type=str, help="Path to temporal chunked zarr collection")
    parser.add_argument("spatial_chunked_collection", type=str, help="Path to spatial chunked zarr collection")
    parser.add_argument("temp_collection", type=str, help="Temporary zarr collection")
    args = parser.parse_args()
    main(args)


import logging
from collections.abc import Iterable

from django import forms
from django.contrib.auth.models import Group
from django.core.exceptions import SuspiciousOperation, ValidationError
from django.db import transaction
from django.db.models import Max, Q
from django.forms.models import BaseInlineFormSet
from django.forms.widgets import CheckboxSelectMultiple
from django.http.request import QueryDict
from django.utils.text import normalize_newlines
from django.utils.translation import gettext_lazy as _

from evap.evaluation.forms import UserModelChoiceField, UserModelMultipleChoiceField
from evap.evaluation.models import (
    Contribution,
    Course,
    CourseType,
    Degree,
    EmailTemplate,
    Evaluation,
    FaqQuestion,
    FaqSection,
    Infotext,
    Question,
    Questionnaire,
    QuestionType,
    RatingAnswerCounter,
    Semester,
    TextAnswer,
    UserProfile,
)
from evap.evaluation.tools import clean_email, date_to_datetime
from evap.results.tools import STATES_WITH_RESULT_TEMPLATE_CACHING, STATES_WITH_RESULTS_CACHING, cache_results
from evap.results.views import update_template_cache, update_template_cache_of_published_evaluations_in_course
from evap.staff.tools import remove_user_from_represented_and_ccing_users
from evap.student.models import TextAnswerWarning

logger = logging.getLogger(__name__)

def disable_all_fields(form):
    for field in form.fields.values():
        field.disabled = True

class CharArrayField(forms.Field):
    hidden_widget = forms.MultipleHiddenInput
    widget = forms.SelectMultiple
    default_error_messages = {
        "invalid_list": _("Enter a list of values."),
    }

    def __init__(self, base_field, *, max_length=None, **kwargs):
        super().__init__(**kwargs)
        assert isinstance(base_field, forms.CharField)
        assert max_length is None

    def to_python(self, value):
        if not value:
            return []
        if not isinstance(value, Iterable):
            raise ValidationError(self.error_messages["invalid_list"], code="invalid_list")
        return [str(val) for val in value]

    def get_bound_field(self, form, field_name):
        return BoundCharArrayField(form, self, field_name)

class BoundCharArrayField(forms.BoundField):
    def as_widget(self, widget=None, attrs=None, only_initial=False):
        widget = widget or self.field.widget
        # Inject all current values as choices so they don’t get discarded
        if self.value():
            widget.choices = [(value, value) for value in self.value()]
        return super().as_widget(widget, attrs, only_initial)

class ModelWithImportNamesFormset(forms.BaseModelFormSet):
    """
    A form set which validates that import names are not duplicated
    """

    def clean(self):
        super().clean()
        import_names = set()
        for form in self.forms:
            if self.can_delete and self._should_delete_form(form):
                continue
            for import_name in form.cleaned_data.get("import_names", []):
                if import_name.lower() in import_names:
                    form.add_error(
                        "import_names",
                        _('Import name "{}" is duplicated. Import names are not case sensitive.').format(import_name),
                    )
                import_names.add(import_name.lower())

class ImportForm(forms.Form):
    use_required_attribute = False

    vote_start_datetime = forms.DateTimeField(label=_("Start of evaluation"), localize=True, required=False)
    vote_end_date = forms.DateField(label=_("End of evaluation"), localize=True, required=False)

    excel_file = forms.FileField(
        label=_("Excel file"),
        required=False,
        widget=forms.FileInput(attrs={"accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}),
    )

class UserImportForm(forms.Form):
    use_required_attribute = False

    excel_file = forms.FileField(
        label=_("Excel file"),
        required=False,
        widget=forms.FileInput(attrs={"accept": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}),
    )

class EvaluationParticipantCopyForm(forms.Form):
    evaluation = forms.ModelChoiceField(
        Evaluation.objects.all(), empty_label="<empty>", required=False, label=_("Evaluation")
    )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.evaluation_selection_required = False

        # Here we split the evaluations by semester and create supergroups for them. We also make sure to include an empty option.
        choices = [("", "<empty>")]
        for semester in Semester.objects.all():
            evaluation_choices = [
                (evaluation.pk, evaluation.full_name)
                for evaluation in Evaluation.objects.filter(course__semester=semester)
            ]
            if evaluation_choices:
                choices += [(semester.name, evaluation_choices)]

        self.fields["evaluation"].choices = choices

    def clean(self):
        if self.evaluation_selection_required and self.cleaned_data["evaluation"] is None:
            raise ValidationError(_("Please select an evaluation from the dropdown menu."))

class UserBulkUpdateForm(forms.Form):
    use_required_attribute = False

    user_file = forms.FileField(label=_("User file"), required=False)

class SemesterForm(forms.ModelForm):
    class Meta:
        model = Semester
        fields = ("name_de", "name_en", "short_name_de", "short_name_en")

    def save(self, commit=True):
        semester = super().save(commit)
        if "short_name_en" in self.changed_data or "short_name_de" in self.changed_data:
            update_template_cache(semester.evaluations.filter(state__in=STATES_WITH_RESULT_TEMPLATE_CACHING))
        return semester

class DegreeForm(forms.ModelForm):
    class Meta:
        model = Degree
        fields = ("name_de", "name_en", "import_names", "order")
        field_classes = {
            "import_names": CharArrayField,
        }
        widgets = {
            "order": forms.HiddenInput(),
        }

    def clean(self):
        super().clean()
        if self.cleaned_data.get("DELETE") and not self.instance.can_be_deleted_by_manager:
            raise SuspiciousOperation("Deleting degree not allowed")

    def save(self, *args, **kwargs):
        degree = super().save(*args, **kwargs)
        if "name_en" in self.changed_data or "name_de" in self.changed_data:
            update_template_cache(
                Evaluation.objects.filter(state__in=STATES_WITH_RESULT_TEMPLATE_CACHING, course__degrees__in=[degree])
            )
        return degree

class CourseTypeForm(forms.ModelForm):
    class Meta:
        model = CourseType
        fields = ("name_de", "name_en", "import_names", "order")
        field_classes = {
            "import_names": CharArrayField,
        }
        widgets = {
            "order": forms.HiddenInput(),
        }

    def clean(self):
        super().clean()
        if self.cleaned_data.get("DELETE") and not self.instance.can_be_deleted_by_manager:
            raise SuspiciousOperation("Deleting course type not allowed")

    def save(self, *args, **kwargs):
        course_type = super().save(*args, **kwargs)
        if "name_en" in self.changed_data or "name_de" in self.changed_data:
            update_template_cache(
                Evaluation.objects.filter(state__in=STATES_WITH_RESULT_TEMPLATE_CACHING, course__type=course_type)
            )
        return course_type

class CourseTypeMergeSelectionForm(forms.Form):
    main_type = forms.ModelChoiceField(CourseType.objects.all())
    other_type = forms.ModelChoiceField(CourseType.objects.all())

    def clean(self):
        super().clean()
        if self.cleaned_data.get("main_type") == self.cleaned_data.get("other_type"):
            raise ValidationError(_("You must select two different course types."))

class CourseFormMixin:
    class Meta:
        model = Course
        fields = (
            "name_de",
            "name_en",
            "type",
            "degrees",
            "responsibles",
            "is_private",
            "semester",
        )
        field_classes = {
            "responsibles": UserModelMultipleChoiceField,
        }

    def _set_responsibles_queryset(self, existing_course=None):
        queryset = UserProfile.objects.exclude(is_active=False)
        if existing_course:
            queryset = (queryset | existing_course.responsibles.all()).distinct()
        self.fields["responsibles"].queryset = queryset

    def validate_unique(self):
        super().validate_unique()
        # name_xy and semester are unique together. This will be treated as a non-field-error since two
        # fields are involved. Since we only show the name_xy field to the user, assign that error to this
        # field. This hack is not documented, so it might be broken when you are reading this.
        for e in self.non_field_errors().as_data():
            if e.code == "unique_together" and "unique_check" in e.params:
                if "semester" in e.params["unique_check"]:
                    # The order of the fields is probably determined by the unique_together constraints in the Course class.
                    name_field = e.params["unique_check"][1]
                    self.add_error(name_field, e)

class CourseForm(CourseFormMixin, forms.ModelForm):  # type: ignore
    semester = forms.ModelChoiceField(Semester.objects.all(), disabled=True, required=False, widget=forms.HiddenInput())

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._set_responsibles_queryset(self.instance if self.instance.pk else None)
        if not self.instance.can_be_edited_by_manager:
            disable_all_fields(self)

class CourseCopyForm(CourseFormMixin, forms.ModelForm):  # type: ignore
    semester = forms.ModelChoiceField(Semester.objects.all())
    vote_start_datetime = forms.DateTimeField(label=_("Start of evaluations"), localize=True)
    vote_end_date = forms.DateField(label=_("Last day of evaluations"), localize=True)

    field_order = ["semester"]

    def __init__(self, data=None, *, instance: Course):
        self.old_course = instance
        opts = self._meta
        initial = forms.models.model_to_dict(instance, opts.fields, opts.exclude)
        super().__init__(data=data, initial=initial)
        self._set_responsibles_queryset(instance)

    # To ensure we don't forget about copying a relevant field, we explicitly list copied and ignored fields and test against that
    EVALUATION_COPIED_FIELDS = {
        "name_de",
        "name_en",
        "weight",
        "is_single_result",
        "is_rewarded",
        "is_midterm_evaluation",
        "allow_editors_to_edit",
        "wait_for_grade_upload_before_publishing",
    }

    EVALUATION_EXCLUDED_FIELDS = {
        "id",
        "course",
        "vote_start_datetime",
        "vote_end_date",
        "participants",
        "contributions",
        "state",
        "can_publish_text_results",
        "_participant_count",
        "_voter_count",
        "voters",
        "votetimestamp",
    }

    CONTRIBUTION_COPIED_FIELDS = {
        "contributor",
        "role",
        "textanswer_visibility",
        "label",
        "order",
    }

    CONTRIBUTION_EXCLUDED_FIELDS = {
        "id",
        "evaluation",
        "ratinganswercounter_set",
        "textanswer_set",
        "questionnaires",
    }

    @transaction.atomic()
    def save(self, commit=True) -> Course:
        new_course: Course = super().save()
        # we need to create copies of evaluations and their participation as well
        for old_evaluation in self.old_course.evaluations.exclude(is_single_result=True):
            new_evaluation = Evaluation(
                **{field: getattr(old_evaluation, field) for field in self.EVALUATION_COPIED_FIELDS},
                can_publish_text_results=False,
                course=new_course,
                vote_start_datetime=self.cleaned_data["vote_start_datetime"],
                vote_end_date=self.cleaned_data["vote_end_date"],
            )
            new_evaluation.save()

            new_evaluation.contributions.all().delete()  # delete default general contribution
            for old_contribution in old_evaluation.contributions.all():
                new_contribution = Contribution(
                    **{field: getattr(old_contribution, field) for field in self.CONTRIBUTION_COPIED_FIELDS},
                    evaluation=new_evaluation,
                )
                new_contribution.save()
                new_contribution.questionnaires.set(old_contribution.questionnaires.all())

        return new_course

class EvaluationForm(forms.ModelForm):
    general_questionnaires = forms.ModelMultipleChoiceField(
        Questionnaire.objects.general_questionnaires().exclude(visibility=Questionnaire.Visibility.HIDDEN),
        widget=CheckboxSelectMultiple,
        label=_("General questions"),
    )

    class Meta:
        model = Evaluation
        fields = (
            "course",
            "name_de",
            "name_en",
            "weight",
            "allow_editors_to_edit",
            "is_rewarded",
            "is_midterm_evaluation",
            "wait_for_grade_upload_before_publishing",
            "vote_start_datetime",
            "vote_end_date",
            "participants",
            "general_questionnaires",
        )
        localized_fields = ("vote_start_datetime", "vote_end_date")
        field_classes = {
            "participants": UserModelMultipleChoiceField,
        }

    def __init__(self, *args, **kwargs):
        semester = kwargs.pop("semester", None)
        super().__init__(*args, **kwargs)
        self.fields["course"].queryset = Course.objects.filter(semester=semester)

        visible_questionnaires = Q(visibility__in=(Questionnaire.Visibility.MANAGERS, Questionnaire.Visibility.EDITORS))
        if self.instance.pk is not None:
            visible_questionnaires |= Q(contributions__evaluation=self.instance)
        self.fields["general_questionnaires"].queryset = (
            Questionnaire.objects.general_questionnaires().filter(visible_questionnaires).distinct()
        )

        queryset = UserProfile.objects.exclude(is_active=False)
        if self.instance.pk is not None:
            queryset = (queryset | self.instance.participants.all()).distinct()
        self.fields["participants"].queryset = queryset

        if self.instance.general_contribution:
            self.fields["general_questionnaires"].initial = [
                q.pk for q in self.instance.general_contribution.questionnaires.all()
            ]

        if Evaluation.State.IN_EVALUATION <= self.instance.state <= Evaluation.State.REVIEWED:
            self.fields["vote_start_datetime"].disabled = True

        if self.instance.pk and not self.instance.can_be_edited_by_manager:
            disable_all_fields(self)

        if self.instance.pk:
            self.instance.old_course = self.instance.course

    def validate_unique(self):
        super().validate_unique()
        # name_xy and course are unique together. This will be treated as a non-field-error since two
        # fields are involved. Since we only show the name_xy field to the user, assign that error to this
        # field. This hack is not documented, so it might be broken when you are reading this.
        for e in self.non_field_errors().as_data():
            if e.code == "unique_together" and "unique_check" in e.params:
                if "course" in e.params["unique_check"]:
                    # The order of the fields is probably determined by the unique_together constraints in the Evaluation class.
                    name_field = e.params["unique_check"][1]
                    self.add_error(name_field, e)

    def clean_participants(self):
        participants = self.cleaned_data.get("participants")
        if self.instance.pk:
            voters = self.instance.voters.all()
            removed_voters = set(voters) - set(participants)
            if removed_voters:
                names = [str(user) for user in removed_voters]
                self.add_error(
                    "participants",
                    _("Participants who already voted for the evaluation can't be removed: %s") % ", ".join(names),
                )

        return participants

    def clean_weight(self):
        weight = self.cleaned_data.get("weight")
        course = self.cleaned_data.get("course")
        if weight == 0 and not course.evaluations.exclude(pk=self.instance.pk).filter(weight__gt=0).exists():
            self.add_error("weight", _("At least one evaluation of the course must have a weight greater than 0."))
        return weight

    def clean(self):
        super().clean()

        vote_start_datetime = self.cleaned_data.get("vote_start_datetime")
        vote_end_date = self.cleaned_data.get("vote_end_date")
        if vote_start_datetime and vote_end_date and vote_start_datetime.date() > vote_end_date:
            self.add_error("vote_start_datetime", "")
            self.add_error("vote_end_date", _("The first day of evaluation must be before the last one."))

    def save(self, *args, **kw):
        evaluation = super().save(*args, **kw)
        selected_questionnaires = self.cleaned_data.get("general_questionnaires")
        removed_questionnaires = set(self.instance.general_contribution.questionnaires.all()) - set(
            selected_questionnaires
        )
        evaluation.general_contribution.remove_answers_to_questionnaires(removed_questionnaires)
        evaluation.general_contribution.questionnaires.set(selected_questionnaires)
        if hasattr(self.instance, "old_course"):
            if self.instance.old_course != evaluation.course:
                update_template_cache_of_published_evaluations_in_course(self.instance.old_course)
                update_template_cache_of_published_evaluations_in_course(evaluation.course)
        return evaluation

class EvaluationCopyForm(EvaluationForm):
    def __init__(self, data=None, instance=None):
        opts = self._meta
        initial = forms.models.model_to_dict(instance, opts.fields, opts.exclude)
        initial["general_questionnaires"] = instance.general_contribution.questionnaires.all()
        super().__init__(data=data, initial=initial, semester=instance.course.semester)

class SingleResultForm(forms.ModelForm):
    event_date = forms.DateField(label=_("Event date"), localize=True)
    answer_1 = forms.IntegerField(label=_("# very good"), initial=0)
    answer_2 = forms.IntegerField(label=_("# good"), initial=0)
    answer_3 = forms.IntegerField(label=_("# neutral"), initial=0)
    answer_4 = forms.IntegerField(label=_("# bad"), initial=0)
    answer_5 = forms.IntegerField(label=_("# very bad"), initial=0)

    class Meta:
        model = Evaluation
        fields = (
            "course",
            "name_de",
            "name_en",
            "weight",
            "event_date",
            "answer_1",
            "answer_2",
            "answer_3",
            "answer_4",
            "answer_5",
        )

    def __init__(self, *args, **kwargs):
        semester = kwargs.pop("semester", None)
        super().__init__(*args, **kwargs)
        self.fields["course"].queryset = Course.objects.filter(semester=semester)

        if self.instance.vote_start_datetime:
            self.fields["event_date"].initial = self.instance.vote_start_datetime

        if self.instance.pk and not self.instance.can_be_edited_by_manager:
            disable_all_fields(self)

        if self.instance.pk:
            for answer_counter in self.instance.ratinganswer_counters:
                self.fields[f"answer_{answer_counter.answer}"].initial = answer_counter.count
            self.instance.old_course = self.instance.course

    def validate_unique(self):
        super().validate_unique()
        # name_xy and course are unique together. This will be treated as a non-field-error since two
        # fields are involved. Since we only show the name_xy field to the user, assign that error to this
        # field. This hack is not documented, so it might be broken when you are reading this.
        for e in self.non_field_errors().as_data():
            if e.code == "unique_together" and "unique_check" in e.params:
                if "course" in e.params["unique_check"]:
                    # The order of the fields is probably determined by the unique_together constraints in the Evaluation class.
                    name_field = e.params["unique_check"][1]
                    self.add_error(name_field, e)

    def save(self, *args, **kw):
        event_date = self.cleaned_data["event_date"]
        self.instance.vote_start_datetime = date_to_datetime(event_date)
        self.instance.vote_end_date = event_date
        self.instance.is_single_result = True
        evaluation = super().save(*args, **kw)

        single_result_questionnaire = Questionnaire.single_result_questionnaire()
        single_result_question = single_result_questionnaire.questions.first()

        contribution, created = Contribution.objects.get_or_create(evaluation=evaluation, contributor=None)
        if created:
            contribution.questionnaires.add(single_result_questionnaire)
        contribution.save()

        # set answers
        contribution = Contribution.objects.get(evaluation=evaluation)
        total_votes = 0
        for i in range(1, 6):
            count = self.cleaned_data["answer_" + str(i)]
            total_votes += count
            RatingAnswerCounter.objects.update_or_create(
                contribution=contribution, question=single_result_question, answer=i, defaults={"count": count}
            )
        evaluation._participant_count = total_votes
        evaluation._voter_count = total_votes

        # change state to Evaluation.State.REVIEWED
        # works only for single_results so the evaluation and its contribution must be saved first
        evaluation.skip_review_single_result()
        evaluation.save()

        if hasattr(self.instance, "old_course"):
            if self.instance.old_course != evaluation.course:
                update_template_cache_of_published_evaluations_in_course(self.instance.old_course)
                update_template_cache_of_published_evaluations_in_course(evaluation.course)

        return evaluation

class ContributionForm(forms.ModelForm):
    contributor = UserModelChoiceField(queryset=UserProfile.objects.exclude(is_active=False))
    evaluation = forms.ModelChoiceField(
        Evaluation.objects.all(), disabled=True, required=False, widget=forms.HiddenInput()
    )
    questionnaires = forms.ModelMultipleChoiceField(
        Questionnaire.objects.contributor_questionnaires().exclude(visibility=Questionnaire.Visibility.HIDDEN),
        required=False,
        widget=CheckboxSelectMultiple,
        label=_("Questionnaires"),
    )
    does_not_contribute = forms.BooleanField(required=False, label=_("Add person without questions"))

    class Meta:
        model = Contribution
        fields = ("evaluation", "contributor", "questionnaires", "role", "textanswer_visibility", "label", "order")
        widgets = {
            "order": forms.HiddenInput(),
            # RadioSelects are necessary so each value gets a id_for_label, see #1769.
            "role": forms.RadioSelect(),
            "textanswer_visibility": forms.RadioSelect(),
        }

    def __init__(self, *args, evaluation=None, **kwargs):
        self.evaluation = evaluation
        # work around https://code.djangoproject.com/ticket/25880
        if self.evaluation is None:
            assert "instance" in kwargs
            self.evaluation = kwargs["instance"].evaluation

        super().__init__(*args, **kwargs)

        if self.instance.contributor:
            self.fields["contributor"].queryset |= UserProfile.objects.filter(pk=self.instance.contributor.pk)

        self.fields["questionnaires"].queryset = (
            Questionnaire.objects.contributor_questionnaires()
            .filter(
                Q(visibility=Questionnaire.Visibility.MANAGERS)
                | Q(visibility=Questionnaire.Visibility.EDITORS)
                | Q(contributions__evaluation=(self.evaluation if self.evaluation.pk else None))
            )
            .distinct()
        )

        if self.instance.pk:
            self.fields["does_not_contribute"].initial = not self.instance.questionnaires.exists()

        if self.evaluation.pk and not self.evaluation.can_be_edited_by_manager:
            # form is used as read-only evaluation view
            disable_all_fields(self)

    def clean(self):
        if not self.cleaned_data.get("does_not_contribute") and not self.cleaned_data.get("questionnaires"):
            self.add_error("does_not_contribute", _("Select either this option or at least one questionnaire!"))

    @property
    def show_delete_button(self):
        if self.instance.pk is None:
            return True  # not stored in the DB. Required so temporary instances in the formset can be deleted.

        if not self.instance.ratinganswercounter_set.exists() and not self.instance.textanswer_set.exists():
            return True

        return False

    def save(self, *args, **kwargs):
        if self.instance.pk:
            selected_questionnaires = self.cleaned_data.get("questionnaires")
            removed_questionnaires = set(self.instance.questionnaires.all()) - set(selected_questionnaires)
            self.instance.remove_answers_to_questionnaires(removed_questionnaires)
        return super().save(*args, **kwargs)

class ContributionCopyForm(ContributionForm):
    def __init__(self, data=None, instance=None, evaluation=None, **kwargs):
        initial = None
        copied_instance = Contribution(evaluation=evaluation)
        if instance:
            opts = self._meta
            initial = forms.models.model_to_dict(instance, opts.fields, opts.exclude)
            del initial["evaluation"]
            initial["does_not_contribute"] = not instance.questionnaires.exists()
        super().__init__(data, initial=initial, instance=copied_instance, evaluation=evaluation, **kwargs)

class EvaluationEmailForm(forms.Form):
    recipients = forms.MultipleChoiceField(
        widget=forms.CheckboxSelectMultiple(), choices=EmailTemplate.Recipients.choices, label=_("Send email to")
    )
    subject = forms.CharField(label=_("Subject"))
    plain_content = forms.CharField(widget=forms.Textarea(), label=_("Plain Text"))
    html_content = forms.CharField(widget=forms.Textarea(), label=_("HTML"))

    def __init__(self, *args, evaluation, export=False, **kwargs):
        super().__init__(*args, **kwargs)
        self.template = EmailTemplate()
        self.evaluation = evaluation
        self.recipient_groups = None
        self.fields["subject"].required = not export
        self.fields["plain_content"].required = not export
        self.fields["html_content"].required = False

    def clean(self):
        self.recipient_groups = self.cleaned_data.get("recipients")

        if not self.recipient_groups:
            raise forms.ValidationError(_("No recipient selected. Choose at least one group of recipients."))

        return self.cleaned_data

    def email_addresses(self):
        recipients = self.template.recipient_list_for_evaluation(
            self.evaluation, self.recipient_groups, filter_users_in_cc=False
        )
        return set(user.email for user in recipients if user.email)

    def send(self, request):
        self.template.subject = self.cleaned_data.get("subject")
        self.template.plain_content = self.cleaned_data.get("plain_content")
        self.template.html_content = self.cleaned_data.get("html_content")
        self.template.send_to_users_in_evaluations(
            [self.evaluation], self.recipient_groups, use_cc=True, request=request
        )

class RemindResponsibleForm(forms.Form):
    to = UserModelChoiceField(None, required=False, disabled=True, label=_("To"))
    cc = UserModelMultipleChoiceField(None, required=False, disabled=True, label=_("CC"))
    subject = forms.CharField(label=_("Subject"))
    plain_content = forms.CharField(widget=forms.Textarea(), label=_("Plain Text"))
    html_content = forms.CharField(widget=forms.Textarea(), label=_("HTML"))

    def __init__(self, *args, responsible, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["to"].initial = responsible.pk
        self.fields["to"].queryset = UserProfile.objects.filter(pk=responsible.pk)
        self.fields["cc"].initial = responsible.cc_users.all() | responsible.delegates.all()
        self.fields["cc"].queryset = responsible.cc_users.all() | responsible.delegates.all()

        self.template = EmailTemplate.objects.get(name=EmailTemplate.EDITOR_REVIEW_REMINDER)
        self.fields["subject"].initial = self.template.subject
        self.fields["plain_content"].initial = self.template.plain_content
        self.fields["html_content"].initial = self.template.html_content

    def send(self, request, evaluations):
        recipient = self.cleaned_data.get("to")
        self.template.subject = self.cleaned_data.get("subject")
        self.template.plain_content = self.cleaned_data.get("plain_content")
        self.template.html_content = self.cleaned_data.get("html_content")
        subject_params = {}
        body_params = {"user": recipient, "evaluations": evaluations}
        self.template.send_to_user(recipient, subject_params, body_params, use_cc=True, request=request)

class QuestionnaireForm(forms.ModelForm):
    class Meta:
        model = Questionnaire
        widgets = {"order": forms.HiddenInput()}
        fields = (
            "type",
            "name_de",
            "name_en",
            "description_de",
            "description_en",
            "public_name_de",
            "public_name_en",
            "teaser_de",
            "teaser_en",
            "order",
            "visibility",
            "is_locked",
        )

    def save(self, *args, commit=True, force_highest_order=False, **kwargs):
        # get instance that has all the changes from the form applied, dont write to database
        questionnaire_instance = super().save(*args, commit=False, **kwargs)

        if force_highest_order or "type" in self.changed_data:
            highest_existing_order = (
                Questionnaire.objects.filter(type=questionnaire_instance.type).aggregate(Max("order"))["order__max"]
                or -1
            )
            questionnaire_instance.order = highest_existing_order + 1

        if commit:
            questionnaire_instance.save(*args, **kwargs)

        return questionnaire_instance

class AtLeastOneFormset(BaseInlineFormSet):
    def clean(self):
        super().clean()
        count = 0
        for form in self.forms:
            if form.cleaned_data and not form.cleaned_data.get("DELETE", False):
                count += 1

        if count < 1:
            raise forms.ValidationError(_("You must have at least one of these."))

class ContributionFormset(BaseInlineFormSet):
    def __init__(self, data=None, **kwargs):
        data = self.handle_moved_contributors(data, **kwargs)
        super().__init__(data, **kwargs)
        if self.instance.pk is not None:
            self.queryset = self.instance.contributions.exclude(contributor=None)

    def handle_deleted_and_added_contributions(self):
        """
        If a contributor got removed and added in the same formset, django would usually complain
        when validating the added form, as it does not check whether the existing contribution was deleted.
        This method works around that.
        """
        for form_with_errors in self.forms:
            if not form_with_errors.errors:
                continue
            if "contributor" not in form_with_errors.cleaned_data:
                continue
            for deleted_form in self.forms:
                if (
                    not deleted_form.cleaned_data
                    or "contributor" not in deleted_form.cleaned_data
                    or not deleted_form.cleaned_data.get("DELETE")
                ):
                    continue
                if not deleted_form.cleaned_data["contributor"] == form_with_errors.cleaned_data["contributor"]:
                    continue
                form_with_errors.instance = deleted_form.instance
                # we modified the form, so we have to force re-validation
                form_with_errors.full_clean()

    @staticmethod
    def handle_moved_contributors(data, **kwargs):
        """
        Work around https://code.djangoproject.com/ticket/25139
        Basically, if the user assigns a contributor who already has a contribution to a new contribution,
        this moves the contributor (and all the data of the new form they got assigned to) back to the original contribution.
        """
        if data is None or "instance" not in kwargs:
            return data

        evaluation = kwargs["instance"]
        if evaluation and not evaluation.pk:
            evaluation = None

        total_forms = int(data["contributions-TOTAL_FORMS"])
        for i in range(0, total_forms):
            prefix = "contributions-" + str(i) + "-"
            current_id = data.get(prefix + "id", "")
            contributor = data.get(prefix + "contributor", "")
            if contributor == "":
                continue
            # find the contribution that the contributor had before the user messed with it
            try:
                previous_id = str(Contribution.objects.get(contributor=contributor, evaluation=evaluation).id)
            except Contribution.DoesNotExist:
                continue

            if current_id == previous_id:
                continue

            # find the form with that previous contribution and then swap the contributions
            for j in range(0, total_forms):
                other_prefix = "contributions-" + str(j) + "-"
                other_id = data[other_prefix + "id"]
                if other_id == previous_id:
                    # swap all the data. the contribution's ids stay in place.
                    data2 = data.copy()
                    data = QueryDict(mutable=True)
                    for key, value in data2.lists():
                        if not key.endswith("-id"):
                            key = (
                                key.replace(prefix, "%temp%")
                                .replace(other_prefix, prefix)
                                .replace("%temp%", other_prefix)
                            )
                        data.setlist(key, value)
                    break
        return data

    def clean(self):
        self.handle_deleted_and_added_contributions()
        found_contributor = set()
        for form in self.forms:
            if not form.cleaned_data or form.cleaned_data.get("DELETE"):
                continue
            contributor = form.cleaned_data.get("contributor")
            if contributor is None:
                raise forms.ValidationError(
                    _("Please select the name of each added contributor. Remove empty rows if necessary.")
                )
            if contributor and contributor in found_contributor:
                raise forms.ValidationError(
                    _("Duplicate contributor ({}) found. Each contributor should only be used once.").format(
                        contributor.full_name
                    )
                )
            if contributor:
                found_contributor.add(contributor)
        super().clean()

class ContributionCopyFormset(ContributionFormset):
    def __init__(self, data, instance, new_instance):
        # First, pass the old evaluation instance to create a ContributionCopyForm for each contribution
        super().__init__(data, instance=instance, form_kwargs={"evaluation": new_instance})
        # Then, use the new evaluation instance as target for validation and saving purposes
        self.instance = new_instance

    def save(self, commit=True):
        # As the contained ContributionCopyForm have not-yet-saved instances,
        # they’d be skipped when saving the formset.
        # To circumvent this, explicitly note that all forms should be saved as new instance.
        self.save_as_new = True
        super().save(commit)

class QuestionForm(forms.ModelForm):
    class Meta:
        model = Question
        fields = ("order", "questionnaire", "text_de", "text_en", "type", "allows_additional_textanswers")
        widgets = {
            "text_de": forms.Textarea(attrs={"rows": 2}),
            "text_en": forms.Textarea(attrs={"rows": 2}),
            "order": forms.HiddenInput(),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.instance.pk and self.instance.type in [QuestionType.TEXT, QuestionType.HEADING]:
            self.fields["allows_additional_textanswers"].disabled = True

    def clean(self):
        super().clean()
        if self.cleaned_data.get("type") in [QuestionType.TEXT, QuestionType.HEADING]:
            self.cleaned_data["allows_additional_textanswers"] = False
        return self.cleaned_data

class QuestionnairesAssignForm(forms.Form):
    def __init__(self, *args, course_types, **kwargs):
        super().__init__(*args, **kwargs)

        for course_type in course_types:
            self.fields[course_type.name] = forms.ModelMultipleChoiceField(
                required=False,
                queryset=Questionnaire.objects.general_questionnaires().exclude(
                    visibility=Questionnaire.Visibility.HIDDEN
                ),
            )
        contributor_questionnaires = Questionnaire.objects.contributor_questionnaires().exclude(
            visibility=Questionnaire.Visibility.HIDDEN
        )
        self.fields["all-contributors"] = forms.ModelMultipleChoiceField(
            label=_("All contributors"), required=False, queryset=contributor_questionnaires
        )

class UserForm(forms.ModelForm):
    is_manager = forms.BooleanField(required=False, label=_("Manager"))
    is_grade_publisher = forms.BooleanField(required=False, label=_("Grade publisher"))
    is_reviewer = forms.BooleanField(required=False, label=_("Reviewer"))
    is_inactive = forms.BooleanField(required=False, label=_("Inactive"))
    evaluations_participating_in = forms.ModelMultipleChoiceField(
        None, required=False, label=_("Evaluations participating in (active semester)")
    )

    class Meta:
        model = UserProfile
        fields = (
            "title",
            "first_name_chosen",
            "first_name_given",
            "last_name",
            "email",
            "delegates",
            "cc_users",
            "is_proxy_user",
        )
        field_classes = {
            "delegates": UserModelMultipleChoiceField,
            "cc_users": UserModelMultipleChoiceField,
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        evaluations_in_active_semester = Evaluation.objects.filter(course__semester=Semester.active_semester())
        excludes = [x.id for x in evaluations_in_active_semester if x.is_single_result]
        evaluations_in_active_semester = evaluations_in_active_semester.exclude(id__in=excludes)
        self.fields["evaluations_participating_in"].queryset = evaluations_in_active_semester
        self.remove_messages = []
        if self.instance.pk:
            self.fields["evaluations_participating_in"].initial = evaluations_in_active_semester.filter(
                participants=self.instance
            )
            self.fields["is_manager"].initial = self.instance.is_manager
            self.fields["is_grade_publisher"].initial = self.instance.is_grade_publisher
            self.fields["is_reviewer"].initial = self.instance.is_reviewer
            self.fields["is_inactive"].initial = not self.instance.is_active

    def clean_evaluations_participating_in(self):
        evaluations_participating_in = self.cleaned_data.get("evaluations_participating_in")
        if self.instance.pk:
            evaluations_voted_for = self.instance.evaluations_voted_for.filter(
                course__semester=Semester.active_semester()
            )
            removed_evaluations_voted_for = set(evaluations_voted_for) - set(evaluations_participating_in)
            if removed_evaluations_voted_for:
                names = [str(evaluation) for evaluation in removed_evaluations_voted_for]
                self.add_error(
                    "evaluations_participating_in",
                    _("Evaluations for which the user already voted can't be removed: %s") % ", ".join(names),
                )

        return evaluations_participating_in

    def clean_email(self):
        email = clean_email(self.cleaned_data.get("email"))
        if email is None:
            return None

        user_with_same_email = UserProfile.objects.filter(email__iexact=email)

        # make sure we don't take the instance itself into account
        if self.instance and self.instance.pk:
            user_with_same_email = user_with_same_email.exclude(pk=self.instance.pk)

        if user_with_same_email.exists():
            raise forms.ValidationError(_("A user with the email '%s' already exists") % email)
        return email

    def save(self, *args, **kw):
        super().save(*args, **kw)
        new_evaluation_list = list(
            self.instance.evaluations_participating_in.exclude(course__semester=Semester.active_semester())
        ) + list(self.cleaned_data.get("evaluations_participating_in"))
        self.instance.evaluations_participating_in.set(new_evaluation_list)

        manager_group = Group.objects.get(name="Manager")
        grade_publisher_group = Group.objects.get(name="Grade publisher")
        reviewer_group = Group.objects.get(name="Reviewer")
        if self.cleaned_data.get("is_manager"):
            self.instance.groups.add(manager_group)
        else:
            self.instance.groups.remove(manager_group)

        if self.cleaned_data.get("is_grade_publisher"):
            self.instance.groups.add(grade_publisher_group)
        else:
            self.instance.groups.remove(grade_publisher_group)

        if self.cleaned_data.get("is_reviewer") and not self.cleaned_data.get("is_manager"):
            self.instance.groups.add(reviewer_group)
        else:
            self.instance.groups.remove(reviewer_group)

        self.instance.is_active = not self.cleaned_data.get("is_inactive")

        # remove instance from all other users' delegates and CC users if it is inactive
        self.remove_messages = (
            [] if self.instance.is_active else remove_user_from_represented_and_ccing_users(self.instance)
        )

        # refresh results cache
        if any(
            attribute in self.changed_data
            for attribute in ["first_name_given", "first_name_chosen", "last_name", "title"]
        ):
            evaluations = Evaluation.objects.filter(
                contributions__contributor=self.instance, state__in=STATES_WITH_RESULTS_CACHING
            ).distinct()
            for evaluation in evaluations:
                cache_results(evaluation)

        self.instance.save()
        return self.instance

class UserMergeSelectionForm(forms.Form):
    main_user = UserModelChoiceField(UserProfile.objects.all())
    other_user = UserModelChoiceField(UserProfile.objects.all())

class UserEditSelectionForm(forms.Form):
    user = UserModelChoiceField(UserProfile.objects.all())

class FaqSectionForm(forms.ModelForm):
    class Meta:
        model = FaqSection
        fields = ("order", "title_de", "title_en")
        widgets = {
            "order": forms.HiddenInput(),
        }

class FaqQuestionForm(forms.ModelForm):
    class Meta:
        model = FaqQuestion
        fields = ("order", "question_de", "question_en", "answer_de", "answer_en")
        widgets = {
            "order": forms.HiddenInput(),
        }

class InfotextForm(forms.ModelForm):
    class Meta:
        model = Infotext
        fields = ("title_de", "title_en", "content_de", "content_en")

class TextAnswerForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields["original_answer"].disabled = "True"
        self.initial["original_answer"] = self.instance.original_answer or self.instance.answer

    class Meta:
        model = TextAnswer
        fields = (
            "answer",
            "original_answer",
        )

    def clean_original_answer(self):
        original_answer = normalize_newlines(self.cleaned_data.get("original_answer"))
        if original_answer == normalize_newlines(self.cleaned_data.get("answer")):
            return None
        return original_answer

class TextAnswerWarningForm(forms.ModelForm):
    class Meta:
        model = TextAnswerWarning
        fields = ("warning_text_de", "warning_text_en", "trigger_strings", "order")
        field_classes = {
            "trigger_strings": CharArrayField,
        }
        widgets = {
            "warning_text_de": forms.Textarea(attrs={"rows": 5}),
            "warning_text_en": forms.Textarea(attrs={"rows": 5}),
            "order": forms.HiddenInput(),
        }

class ExportSheetForm(forms.Form):
    def __init__(self, semester, *args, **kwargs):
        super().__init__(*args, **kwargs)
        degrees = Degree.objects.filter(courses__semester=semester).distinct()
        degree_tuples = [(degree.pk, degree.name) for degree in degrees]
        self.fields["selected_degrees"] = forms.MultipleChoiceField(
            choices=degree_tuples, required=True, widget=forms.CheckboxSelectMultiple(), label=_("Degrees")
        )
        course_types = CourseType.objects.filter(courses__semester=semester).distinct()
        course_type_tuples = [(course_type.pk, course_type.name) for course_type in course_types]
        self.fields["selected_course_types"] = forms.MultipleChoiceField(
            choices=course_type_tuples, required=True, widget=forms.CheckboxSelectMultiple(), label=_("Course types")
        )


from rest_framework.schemas.openapi import SchemaGenerator

from dfirtrack_main.templatetags.dfirtrack_main_tags import dfirtrack_version

class DFIRTrackSchemaGenerator(SchemaGenerator):
    """
    Extend the schema to include some documentation and override not-yet-implemented security, this has been
    copied from https://columbia-it-django-jsonapi-training.readthedocs.io/en/latest/documenting-api/.

    """

    def get_schema(self, request, public):
        schema = super().get_schema(request, public)
        schema['info'] = {
            'title': 'DFIRTrack',
            'description': 'OpenAPI 3 - Documentation of DFIRTrack API',
            'version': dfirtrack_version(),
        }
        # temporarily add securitySchemes until implemented upstream
        if 'securitySchemes' not in schema['components']:
            schema['components']['securitySchemes'] = {
                'basicAuth': {
                    'type': 'http',
                    'scheme': 'basic',
                    'description': 'basic authentication',
                },
                'bearerAuth': {
                    'type': 'http',
                    'scheme': 'bearer',
                    'name': 'Token',
                    'description': 'API Token authentication',
                },
            }

        # temporarily add default security object at top-level
        if 'security' not in schema:
            schema['security'] = [
                {'basicAuth': []},
                {'bearerAuth': []},
            ]

        return schema


from flask import current_app
from werkzeug.local import LocalProxy
from jose import jwt, JWTError

_keycloak = LocalProxy(lambda: current_app.extensions['keycloak'])
_cache = LocalProxy(lambda: _keycloak.cache)

def custom_jwt_validation(token, leeway=0, exclude=None):
    """ perform custom jwt validation

        leeway number of seconds of skew that is allowed
        exclude is a list of claims to exclude from validation.
        jose jwt defaults to:

            defaults = {
                'verify_signature': True,
                'verify_aud': True,
                'verify_iat': True,
                'verify_exp': True,
                'verify_nbf': True,
                'verify_iss': True,
                'verify_sub': True,
                'verify_jti': True,
                'leeway': 0,
            }
    """

    options = {}
    exclude = exclude or []
    for key in exclude:
        options[key] = False

    if leeway != 0:
        options['leeway'] = leeway

    try:
        jwt.decode(
            token,
            _cache.get('keycloak_secret_key'),
            options=options,
            audience=current_app.config['KEYCLOAK_CLIENT_NAME']
        )
        return True
    except JWTError:
        raise
        return False


import string

def rot13(s):
    ans = ''
    for i in s:
        if not i.isalpha():
            ans += i
        else:
            check = ord(i)
            check += 13
            if i.islower() and check > ord('z'):
                check -= 26
            if i.isupper() and check > ord('Z'):
                check -= 26
            ans += chr(check)
    return ans

def rot5(s):
    ans = ''
    for i in s:
        if not i.isdigit():
            ans += i
            continue
        check = ord(i)
        check += 5
        if check > ord('9'):
            check -= 10
        ans += chr(check)
    return ans

def rot18(s):
    return rot5(rot13(s))

def rot47(s):
    ans = ''
    for i in s:
        check = ord(i)
        check -= 47
        if check < 33:
            check += 94
        ans += chr(check)

    return ans

def get_rot(s,key):
    key = int(key)
    if key == 5:
        #print('rot5 = ', rot5(s))
        return rot5(s)
    if key == 18:
        #print('rot18 = ', rot18(s))
        return rot18(s)
    if key == 47:
        #print('rot47 = ', rot47(s))
        return rot47(s)
    if key == 13:
        #print('rot13 = ', rot13(s))
        return rot13(s)

if __name__ == '__main__':
    s = input('字符串:')
    key = input('rot几?')
    print(get_rot(s,key))


def primesfound(a,b):
    total = 0
    for n in range(1,1000):
        rep = n**2 + a*n + b
        if (rep > 0) and (isPrime(rep)):
            total += 1
        else:
            return total
    return total

def isPrime(num):
    if (num <= 1) or (num % 2 == 0):
        return False
    if (num == 2):
        return True
    for x in range(3,num):
        if (num % x == 0):
            return False
    return True

answer = 0
for a in range(-999,1000):
    for b in range(-1000,1001):
        if primesfound(a,b) > answer:
            answer = primesfound(a,b)
            perma = a
            permb = b
print(f"The answer is {answer} from {perma} and {permb} so the Problem's solution is {perma * permb}")


from aiogram import Dispatcher
from aiogram.types import Message, ParseMode

from tgbot.models.dialog import Dialog
from tgbot.models.role import UserRole
from tgbot.services.btn_builder import init_buttons_markup
from tgbot.services.fake_db import DatabaseFaker

DB_DATA = DatabaseFaker.get_data()
markup = init_buttons_markup(DB_DATA)

def get_full_name(m: Message):
    fn = "" if m.from_user.first_name is None else m.from_user.first_name
    ln = "" if m.from_user.last_name is None else m.from_user.last_name
    return "{} {}".format(fn, ln).strip()

async def admin_start(m: Message):
    await m.reply(
        Dialog.WELCOME.value.format(get_full_name(m)),
        reply_markup=markup,
        parse_mode=ParseMode.HTML,
    )

async def handle_text(m: Message):
    await m.bot.send_message(
        chat_id=m.from_user.id,
        text="{} {}".format("HeyðŸ‘‹, there is your text!\n\n", DB_DATA.get(m.text)),
        reply_markup=markup,
    )

def register_admin(dp: Dispatcher):
    dp.register_message_handler(
        admin_start, commands=["start"], state="*", role=[UserRole.ADMIN, UserRole.USER]
    )
    dp.register_message_handler(
        handle_text,
        lambda msg: msg.text in list(DB_DATA.keys()),
        state="*",
        role=[UserRole.ADMIN, UserRole.USER],
    )
    # # or you can pass multiple roles:
    # dp.register_message_handler(admin_start, commands=["start"], state="*", role=[UserRole.ADMIN])
    # # or use another filter:
    # dp.register_message_handler(admin_start, commands=["start"], state="*", is_admin=True)


import asyncio
import aiohttp
from aiohttp import web

routes = web.RouteTableDef()

@routes.get("/getActivity")
async def get_activity(req):
    try:
        async with aiohttp.ClientSession() as session:
            for _ in range(5):
                tasks = []
                for __ in range(8):
                    data = await session.get("https://www.boredapi.com/api/activity")
                    json = await data.json()
                    tasks.append(
                        asyncio.create_task(
                            session.post(
                                "http://localhost:8081/parseActivities", json=json
                            )
                        )
                    )
            response = await asyncio.gather(*tasks)
            response = [await x.json() for x in response]
        return web.json_response(response)
    except Exception as e:
        return web.json_response({"status": "failed", "message": str(e)})

app = web.Application()

app.router.add_routes(routes)

web.run_app(app)


from django.db import models
from django.utils.timezone import now
from realtors.models import Realtor
from multiselectfield import MultiSelectField

class Listing(models.Model):
    MY_CHOICES =(
             ('Outdoor Space', 'Outdoor Space'),
             ('Large Windows and Natural Lighting','Large Windows and Natural Lighting'),
             ('Huge closet Space','Huge closet Space'),
             ('Air Conditioning','Air Conditioning'),
             ('Ample Parking','Ample Parking'),
             ('Swimming Pool','Swimming Pool'),
             ('24hr CCTV ', '24hr CCTV '),
             ('Solar Water Heating', 'Solar Water Heating'),
             ('Laundry Area','Laundry Area'),
             ('Dinning', 'Dinning'),
             ('Open Plan Kitchen', 'Open Plan Kitchen'),
             ('Roof-top', 'Roof-top'),
             ('Borehole','Borehole'),
             ('Spacious''Spacious'),
             ('Scenic View', 'Scenic View'),
             ('Gated Community','Gated Community')
             )
    class SaleType(models.TextChoices):
        FOR_SALE = 'For sale'
        FOR_RENT = 'For rent'
        ACCOMODATION ='Accomodation'
    class PropertyType(models.TextChoices):
        HOME ='Home'
        GUESTHOUSE ='Guesthouse'
        LADIESHOSTELS='Ladies Hostels'
        GENTSHOSTELS='Gents Hostels'
        BEDSITTER='Bedsitter'
        TOWNHOUSE ='Townhouse'
        PLOTS ='Plots'
        WAREHOUSE= 'Warehouse'
        LAND ='Land'
        SHOPS ='Shops'
        OFFICE ='Office'
    # class AvailableType(models.TextChoices):
    #     TRUE = 'True'
    #     FALSE = 'False'

    class LocationTown(models.TextChoices):
        KILIFI ='Kilifi'
        MALINDI ='Malindi'
        MOMBASA='Mombasa'
        NAIROBI ='Nairobi'
        KITALE ='Kitale'
        MTWAPA ='Mtwapa'
        NANYUKI ='Nanyuki'
        KISUMU ='kisumu'

    class Rates(models.TextChoices):
        PER_NIGHT='Per Night'
        PER_MONTH ='Per Month'

    realtor = models.ForeignKey(Realtor, on_delete=models.DO_NOTHING)
    slug = models.CharField(max_length=200, unique=True)
    title= models.CharField(max_length =150)
    address = models.CharField(max_length=50)
    town = models.CharField(max_length= 50, choices =LocationTown.choices, default=LocationTown.KILIFI)
    county = models.CharField(max_length=100)
    sale_type = models.CharField(max_length=50, choices=SaleType.choices, default=SaleType.FOR_SALE)
    price = models.IntegerField()
    bedrooms = models.IntegerField()
    bathrooms =models.IntegerField()
    property_type = models.CharField(max_length= 50, choices =PropertyType.choices, default=PropertyType.HOME)
    description = models.TextField(blank=True)
    rate = models.CharField(max_length=50, choices=Rates.choices,blank=True)
    sqft = models.IntegerField()
    payment_plan=models.CharField(max_length=100, blank=True)
    feature_1= models.CharField(max_length=100, blank=True)
    feature_2= models.CharField(max_length=100, blank=True)
    feature_3= models.CharField(max_length=100, blank=True)
    feature_4= models.CharField(max_length=100, blank=True)
    feature_5= models.CharField(max_length=100, blank=True)
    feature_6= models.CharField(max_length=100, blank=True)
    feature_7= models.CharField(max_length=100, blank=True)
    feature_8= models.CharField(max_length=100, blank=True)
    feature_9= models.CharField(max_length=100, blank=True)
    feature_10= models.CharField(max_length=100, blank=True)
    feature_11= models.CharField(max_length=100, blank=True)
    feature_12= models.CharField(max_length=100, blank=True)
    amenities= MultiSelectField(choices=MY_CHOICES)

    featured = models.BooleanField(default =False)
    # property_availability = models.CharField(max_length=50, choices=AvailableType.choices, default=AvailableType.TRUE)
    open_house = models.BooleanField(default=False)
    photo_main = models.ImageField(upload_to='photos/%Y/%m/%d')
    photo_1 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_2 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_3 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_4 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_5 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_6 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_7 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_8 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_9 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_10 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_11 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_12 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_13 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_14 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_15 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_16 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_17 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_18 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_19 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    photo_20 = models.ImageField(upload_to='photos/%Y/%m/%d', blank=True)
    is_published =models.BooleanField(default=True)
    list_date = models.DateTimeField(default=now, blank=True)

    def __str__(self):
        return self.title


from typing import Any

import fastapi as fa
from database.db_async import get_async_session
from fastapi.responses import FileResponse
from services.telegram.tasks import db_handlers as db_hand
from services.user.dependencies import get_current_user
from services.user.models import User
from settings import config
from sqlalchemy.ext.asyncio import AsyncSession
from utils import files

async def download_file(
    task_name: str = fa.Query(min_length=1),
    session: AsyncSession = fa.Depends(get_async_session),
    current_user: User = fa.Depends(get_current_user),
) -> FileResponse:
    tasks = await db_hand.get_tasks_by_filter(
        session=session,
        data={
            "title": task_name,
            "user_id": current_user.id,
        },
    )
    if not tasks:
        raise fa.HTTPException(
            status_code=fa.status.HTTP_404_NOT_FOUND,
            detail="Такой задачи не существует",
        )
    dir_url = config.files_dir_url / str(current_user.id)
    file_url = await files.get_file_url(
        dir_url=dir_url,
        file_name=task_name,
    )
    return FileResponse(
        path=file_url,
        media_type="application/octet-stream",
        filename=file_url.name,
    )

async def delete_task(
    task_name: str = fa.Query(min_length=1),
    session: AsyncSession = fa.Depends(get_async_session),
    current_user: User = fa.Depends(get_current_user),
) -> fa.Response:
    tasks = await db_hand.get_tasks_by_filter(
        session=session,
        data={
            "title": task_name,
            "user_id": current_user.id,
        },
    )
    if not tasks:
        raise fa.HTTPException(
            status_code=fa.status.HTTP_404_NOT_FOUND,
            detail="Такой задачи не существует",
        )
    task = tasks[0]
    await db_hand.delete_task(
        session=session,
        task_id=task.id,
    )
    dir_url = config.files_dir_url / str(current_user.id)
    await files.delete_file(dir_url=dir_url, file_name=task_name)
    return fa.Response(content="Задача удалена успешно")

async def get_user_tasks(
    session: AsyncSession = fa.Depends(get_async_session),
    current_user: User = fa.Depends(get_current_user),
) -> Any:
    tasks = await db_hand.get_tasks_by_filter(
        session=session,
        data={
            "user_id": current_user.id,
        },
    )
    return tasks

async def get_tasks(
    session: AsyncSession = fa.Depends(get_async_session),
) -> Any:
    tasks = await db_hand.get_tasks_by_filter(
        session=session,
        data={},
    )
    return tasks


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

from abc import ABCMeta, abstractmethod

import sys
sys.path.append('GCN')
from copy import copy

import networkx as nx
# from model_utils import *

LABEL_NAME = 'label'

class GraphModel(metaclass=ABCMeta):
    """
        Class that implements the Graph Model Abstract Class
    """

    # def __init__(self,A,X,y_train,idx_train,params):
    def __init__(self, graph, params):
        """

        :param graph:  networkx Graph
        :param params: dictionary of parametes for the model / data
        """

        self.graph = graph
        self.params = params
        self.LIST_METRICS = {'roc_auc_score', 'accuracy'}
        #super(GraphModel, self).__init__()

    def initialize_graph(self):
        """

        :return: initialize the default parameters of the model
                and extracts data from the graph
        """
        self.set_default_params(self.params)
        self.extract_data_from_nx_graph()

    def set_default_params(self,DEFAULT_PARAMS={}):
        """
        Set the default parameters in case none is provided
        """

        def cast_like(a, b):
            if isinstance(b, int):
                return int(a)
            elif isinstance(b, float):
                return float(a)
            elif isinstance(b, str):
                return str(a)
            else:
                return a

        # FEATURE indicates whether to use FEATURE or not
        # This also depends on the availability of the features
        for key in DEFAULT_PARAMS.keys():
            if key not in self.params.keys():
                self.params[key] = DEFAULT_PARAMS[key]
            else:
                self.params[key] = cast_like(self.params[key], DEFAULT_PARAMS[key])

    @abstractmethod
    def extract_feature_matrix(self):
        '''

        :param graph: nx graph
        :return: the feature matrix or a identity matrix if no features are involved
        '''
        pass

        # if self.num_features >0:
        #     X = np.zeros([self.num_nodes, self.num_features])
        #
        #     for i, node in enumerate(self.nodes_list):
        #         node_features_dict = copy(self.graph.nodes[node])
        #         node_features_dict.pop('label')
        #         for j, var in enumerate(node_features_dict.keys()):
        #             X[i][j] = node_features_dict[var]
        #     return X
        # else:
        #     X = np.eye(self.num_nodes)
        #     return X

    def extract_data_from_nx_graph(self):
        '''

        :param graph: nx graph
        :return: generates the adjacecy matrix, number of nodes, features, and feature matrix
        '''

        self.A = nx.adjacency_matrix(self.graph)
        self.nodes_list = list(self.graph.nodes)
        self.num_nodes = self.A.shape[0]

        assert self.num_nodes == self.graph.number_of_nodes()

        self.feature_names = list(set(self.graph.nodes[list(self.graph.nodes)[0]].keys()).difference({LABEL_NAME}))

        self.num_feature_names = len(self.feature_names)

        self.X = self.extract_feature_matrix()

    # @abstractmethod
    # def fit_model(self):
    #     pass
    #
    # @abstractmethod
    # def train_model(self):
    #     pass
    #
    # @abstractmethod
    # def get_graph_predictions(self):
    #     pass


import collections

import aoc_helper
from aoc_helper import (
    Grid,
    PrioQueue,
    decode_text,
    extract_ints,
    frange,
    irange,
    iter,
    list,
    map,
    range,
    tail_call,
)

raw = aoc_helper.fetch(9, 2022)

VECTOR = {
    "U": (0, -1),
    "D": (0, 1),
    "L": (-1, 0),
    "R": (1, 0),
}

def parse_raw(raw: str):
    return (
        list(raw.splitlines())
        .mapped(lambda i: i.split())
        .mapped(lambda i: (VECTOR[i[0]], int(i[1])))
    )

data = parse_raw(raw)

def move_towards(dest, src):
    dx, dy = dest
    sx, sy = src
    if dx > sx:
        sx += 1
    elif dx < sx:
        sx -= 1
    if dy > sy:
        sy += 1
    elif dy < sy:
        sy -= 1
    return sx, sy

def part_one(data):
    visited = collections.defaultdict(bool)
    head = (0, 0)
    tail = (0, 0)
    for direction, distance in data:
        for _ in range(distance):
            head = (head[0] + direction[0], head[1] + direction[1])
            if list(zip(head, tail)).mapped(lambda i: abs(i[0] - i[1])).max() > 1:
                tail = move_towards(head, tail)
            visited[tail] = True
    return len(visited)

aoc_helper.lazy_test(day=9, year=2022, parse=parse_raw, solution=part_one)

def part_two(data):
    visited = collections.defaultdict(bool)
    head = (0, 0)
    tails = [(0, 0)] * 9
    for direction, distance in data:
        for _ in range(distance):
            head = (head[0] + direction[0], head[1] + direction[1])
            for i, (prev, next) in list([head, *tails]).windowed(2).enumerated():
                if list(zip(prev, next)).mapped(lambda i: abs(i[0] - i[1])).max() > 1:
                    tails[i] = move_towards(prev, next)
            visited[tails[-1]] = True
    return len(visited)

aoc_helper.lazy_test(day=9, year=2022, parse=parse_raw, solution=part_two)

aoc_helper.lazy_submit(day=9, year=2022, solution=part_one, data=data)
aoc_helper.lazy_submit(day=9, year=2022, solution=part_two, data=data)


# BMI = body mass index
# BMI = (weight)/ (height * height )

height = input("Enter your height in meter : ")
weight =  input("Enter your weight in kg : ")

# new_height = float (height)
# new_weight= float(weight)

your_bmi =  ( float(weight) / float( height)* 2 )
new_bmi = float(your_bmi)

print(  new_bmi  )

2. Day 2 - Beginner - Understanding Data Types and How to Manipulate Strings/BMI_calculator.py


from util import *

# CONSTANTS
# =============================================================================
ZOMBIE_VELOCITY = 3.0
ZOMBIE_ACCELERATION = 2.0
ZOMBIE_MASS = 180
ZOMBIE_DRAG = 0.9
ZOMBIE_DIMEN = 30
ZOMBIE_HEALTH = 100
WALK_PROB = [0.125] * 8

FX_FLESH_SHOT = SOUND_FX_DIR + "bullet_flesh.wav"
Z_MOTION = ZOMBIE_SPRITE_DIR + "motion/"
Z_ATTACK = ZOMBIE_SPRITE_DIR + "attack/"
Z_DEAD = ZOMBIE_SPRITE_DIR + "dead/1.png"
# =============================================================================

# Zombie Sprite
# =============================================================================
class Zombie(pygame.sprite.Sprite):
    def __init__(self, dt=0.5):
        pygame.sprite.Sprite.__init__(self)

        self.i = 0
        self.sprite_img = []
        self.bite_img = []
        self.image = None
        self.rect = None
        self.angle = 0
        self.dimen = ZOMBIE_DIMEN
        self.center = np.array([0, 0])

        self.curr_time = 0
        self.dt = dt
        self.pos = np.array([0, 0])
        self.vel = np.array([0, 0])
        self.mag_vel = ZOMBIE_VELOCITY
        self.acc = np.array([0, 0])
        self.mag_acc = ZOMBIE_ACCELERATION
        self.dir = np.array([0, 0])
        self.bull_vel = np.array([0, 0])
        self.bull_mass = 0.0

        self.mass = ZOMBIE_MASS
        self.drag = ZOMBIE_DRAG
        self.health = ZOMBIE_HEALTH

        self.solver = ode(self.f)
        self.solver.set_integrator('dop853')

        self.walks = []
        self.walk_prob = WALK_PROB

        self.seesPlayer = False
        self.alive = True
        self.alert = True

        self.flesh_hit = pygame.mixer.Sound(FX_FLESH_SHOT)
        self.flesh_hit.set_volume(0.3)
        self.biting = False

    def setup(self):
        for img_name in os.listdir(Z_MOTION):
            self.sprite_img.append(os.path.join(Z_MOTION, img_name))

        for img_name in os.listdir(Z_ATTACK):
            self.bite_img.append(os.path.join(Z_ATTACK, img_name))

        self.image = pygame.image.load(self.sprite_img[0])
        self.image = pygame.transform.scale(self.image, (self.dimen, self.dimen))
        self.rect = self.image.get_rect()

        '''
        Velocity in order: up, up-right, right, down-right, down, down-left,
                           left, up-left
        '''
        self.walks = [[0, -1], \
                      [1, -1], \
                      [1, 0],  \
                      [1, 1],  \
                      [0, 1],  \
                      [-1, 1], \
                      [-1, 0], \
                      [-1, -1]]

        self.pos[0] = random.choice([random.randint(0, 100),\
                                     random.randint(WIDTH-100, WIDTH)])
        self.pos[1] = random.choice([random.randint(0, 100), \
                                     random.randint(HEIGHT-100, HEIGHT)])

        self.dir = self.walks[random.randint(0, len(self.walks))]

        # Init RK4 solver
        self.random_walk()
        self.set_pos(self.pos)
        self.set_vel()
        self.set_acc()
        self.solver.set_initial_value([self.pos[0], \
                                       self.pos[1], \
                                       self.vel[0], \
                                       self.vel[1]], self.curr_time)

    def f(self, t, state):
        dx, dy = (state[2:4]*self.mass + 10*self.bull_vel*self.bull_mass)/(self.mass + self.bull_mass)
        dvx, dvy = self.acc - self.drag*state[2:4]

        return [dx, dy, dvx, dvy]

    def update(self):
        if self.alive:
            if not self.seesPlayer:
                self.random_walk()
                self.set_acc()

            if not self.biting or self.bull_mass != 0.0:
                self.curr_time += self.dt
                if self.solver.successful():

                    self.solver.integrate(self.curr_time)
                    pos = self.solver.y[0:2]
                    self.vel = self.solver.y[2:4]
                    self.dir = normalize(self.vel)

                    pos = check_boundary(self, pos)

                    self.set_pos(pos)

            if (self.bull_mass != 0.0):
                self.i += 1

                if self.i > 10:
                    self.i  = 0
                    self.set_bullet()

    def random_walk(self):
        continue_random = random.uniform(0, 1)

        if continue_random < 0.05:
            rand_choice = random.uniform(0, 1)
            cum_prob = np.cumsum(self.walk_prob)

            for i in range(0, len(cum_prob)):
                if rand_choice < cum_prob[i]:
                    direction = self.walks[i]
                    break

            self.dir = normalize(direction)

    def draw(self, screen):
        screen.blit(self.image, self.rect)

    def set_towards_player(self, player):
        self.seesPlayer = True
        self.dir = normalize((player.pos + player.dimen/2) - (self.pos + self.dimen/2))
        self.set_pos(self.pos)
        self.mag_acc *= 2.0
        self.set_acc()
        self.mag_acc /= 2.0

    def set_bullet(self, bullet=None):
        if bullet==None:
            self.bull_vel = np.array([0, 0])
            self.bull_mass = 0.0
        else:
            self.bull_vel = normalize(bullet.vel)*bullet.mag_vel*10
            self.bull_mass = bullet.mass

    def set_pos(self, pos):
        if np.fabs(self.vel[0]) > 0.1 or np.fabs(self.vel[1]) > 0.1:
            if not self.biting:
                animate(self)
            else:
                animate_with(self, self.bite_img)
            rotate(self, self.dir)

        self.pos = pos
        self.rect = self.pos
        self.center[0] = self.pos[0] + self.dimen/2.0
        self.center[1] = self.pos[1] + self.dimen/2.0

    def set_vel(self):
        self.vel = np.multiply(self.dir, self.mag_vel)

    def set_acc(self):
        self.acc = np.multiply(self.dir, self.mag_acc)

    def dec_health(self, damage):
        self.flesh_hit.play()
        self.health -= damage

        if self.health <= 0:
            self.health = 0
            self.alive = False
            self.image = pygame.image.load(Z_DEAD)
# =============================================================================


""""
Author(s): Ashton Johns
This program drives the menus for the user types and the choices associated for each user type.
"""
from rsa_functionality import rsa_functionality

class main():

    operation = rsa_functionality()

    #GET KEYS HERE - REVISE
    operation.generating_RSA_keys()

    dict_of_options = {1:('Please select your user type','1. A public user','2. The owner of the keys','3. Exit program'),
                    2:('As a public user what would you like to do?','1. Send an encrypted message','2. Authenticate a digital signature','3. Exit'),
                    3:('As the owner of the keys what would you like to do?', '1. Decrypt a received message','2. Digitally sign a message','3. Show the keys','4. Generating a new set of the keys','5. Exit'),}

    dict_of_operations = {'2a':operation.send_encrypted_message,
                        '2b':operation.digital_sig,
                        '3a':operation.decrypt_received_message,
                        '3b':operation.digital_sig,
                        '3c':operation.show_keys,
                        '3d':operation.generating_RSA_keys}

    #Start with key here
    key = 1

    #run while loop until cont = false
    cont = True

    while cont:

        # Find printable options in dict_of_options
        if key in dict_of_options:

            #will store the options, and where a choice must be made on the option
            list_of_options = []

            #get the value from the dict_of_options without parenthese [()], quote ['], or comma [,]
            options_string = str(dict_of_options.get(key))
            options_string = options_string.replace("(","")
            options_string = options_string.replace(")","")
            options_string = options_string.replace("'","")

            #add these options to the list
            list_of_options = options_string.split(", ")

            #print the options where a choice must be made
            for seperated_list_of_options in list_of_options:
                if seperated_list_of_options.startswith(".",1):
                    print("\t" + seperated_list_of_options)
                else:
                    print(seperated_list_of_options)

            #user input corresponds to options to choose from
            option = input("\nEnter your choice: ")

            #main menu (start)
            if key == 1:
                if option == '1':
                    key = 2
                elif option == '2':
                    key = 3
                elif option == '3':
                    # exit program
                    cont = False
                else:
                    print("\nPlease select a valid choice.\n")
            #public user
            elif key == 2:
                if option == '1':
                    key = '2a'
                elif option == '2':
                    key = '2b'
                elif option == '3':
                    key = 1
                else:
                    print("\nPlease select a valid choice.\n")
            #key owner
            elif key == 3:
                if option == '1':
                    key = '3a'
                elif option == '2':
                    key = '3b'
                elif option == '3':
                    key = '3c'
                elif option == '4':
                    key = '3d'
                elif option == '5':
                    key = 1
                else:
                    print("\nPlease select a valid choice.\n")
            #invalid choice
            # else:
            #     #reset
            #     print("Wrong selection, back to menu\n")
            #     key = 1

        # Find associated option operations in dict_of_operations
        #public user
        elif key == '2a' or key == '2b':
            #public user
            if key == '2a':
                functn = dict_of_operations[key]
                functn()
                # go back to public user menu
                key = 2
            elif key == '2b':
                functn = dict_of_operations[key]
                functn(authenticate=True)
                key = 2
        #key owner
        else:
            if key == '3a' or key == '3c' or key == '3d':
                functn = dict_of_operations[key]
                functn()
                # go back to key owner menu
                key = 3
            elif key == '3b':
                functn = dict_of_operations[key]
                functn(authenticate=False)
                key = 3

    #Finished, cont = False
    print("\nBye for now!\n")

if __name__ == "__main__":
    main()


__author__ = 'DSL'
# -*-coding:utf-8-*-
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

file = 'data.csv'
df = pd.read_csv(file, header=None)
y = df.loc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)
x = df.iloc[0:100, [0,2]].values
plt.scatter(x[:50, 0], x[:50, 1], color = 'red', marker='o', label = 'setosa')
plt.scatter(x[50:100, 0], x[50:100, 1], color = 'blue', marker='x', label = 'versicolor')
plt.xlabel('花瓣长度')
plt.ylabel('花径长度')
plt.legend(loc = 'upper left')
plt.show()


import xlsxwriter
import math
import sys
import paramiko
import re

cst_rams = [16, 24, 32, 64, 128, 196]

# connect to server and get outputstream
def getConnection(ip, username='zdeploy'):
    if(ip != ''):
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh.connect(hostname = ip, username=username,timeout=2)
            print('*** Connected to:' + ip + ' with username=' + username)
            return ssh
        except IOError as error:
            print('!!! Error connect to ' + ip + ' with username=' +username)
            print(error)
            return None
    return None

def getOutputStreamFromServer(sshClient, cmd):
    if(sshClient is not None):
        try:
            stdin, stdout, stderr = sshClient.exec_command(cmd, timeout=2)
            res = stdout.read()
            return res.decode('utf-8')
        except (paramiko.BadHostKeyException, paramiko.AuthenticationException,
            paramiko.SSHException) as e:
            print('Error when execute command ' + cmd)
            print(e)
            return ''
        return ''
    return ''

# get Ip from text file
def getListIp(pathFile):
    f1 = open(pathFile, 'r')
    data = f1.read()
    arr = data.split('\n')
    return arr

# parseOutputStream to useful data
# parse ram : #b'MemTotal:       16367992 kB\n'
def extractMemInfo(inputStream):
    if(inputStream != ''):
        lenStr= len(inputStream)
        strContent = inputStream[0: lenStr - 1]
        while("  " in strContent):
            strContent = strContent.replace("  ", " ")
        params = strContent.split(' ')
        ram = 0
        if(params[2] == 'B'):
            ram = math.ceil(float(params[1])/(1024*1024*1024))
        elif(params[2] == 'kB'):
            ram = math.ceil(float(params[1])/(1024*1024))
        elif(params[2] == 'mB'):
            ram = math.ceil(float(params[1])/(1024))
        ram = beautifyRam(ram)
        return ram
    return 0

# parse cpu : #b'cpu cores\t: 4\n'
def extractCpuInfo(inputStream):
    if(inputStream != ''):
        lenStr= len(inputStream)
        strContent = inputStream[0: lenStr - 1]
        while("  " in strContent):
            strContent = strContent.replace("  ", " ")
        strContent = strContent.replace('\t', '')
        cpu = 0
        params = strContent.split(':')
        cpu = int(params[1])
        return cpu
    return 0

# parse jdk.version
# b'lrwxrwxrwx 1 root root 30 Th05 14 14:42 /zserver/java/jdk -> /zserver/java/jdk1.8.0_162-x64\n'
# total 335388
# drwxrwxrwx  9 root    root         4096 Mar  2  2011 apache-activemq-5.4.2
# drwxrwxrwx  6 root    root         4096 Dec 20  2010 apache-ant-1.8.2
# drwxrwxrwx 10 root    root         4096 Dec 12  2011 apache-tomcat-7.0.8
# drwxrwxrwx  9 root    root         4096 Mar  2  2011 axis2-1.5.4
# lrwxrwxrwx  1 root    root           11 Nov 17  2015 jdk -> jdk1.6.0_31
# lrwxrwxrwx  1 root    root           11 Nov 17  2015 jdk1.6.0 -> jdk1.6.0_31
# drwxrwxrwx  9 zdeploy es_user      4096 Nov 17  2015 jdk1.6.0_31
# drwxrwxrwx 10 zdeploy es_user      4096 Jun 27  2011 jdk1.7.0
# drwxrwxrwx  8 zdeploy es_user      4096 Oct  8  2013 jdk1.7.0_45-x64
# drwxrwxrwx  8 zdeploy es_user      4096 Dec 19  2013 jdk1.7.0_51-x64
# drwxr-xr-x  8 uucp        143      4096 Apr 11  2015 jdk1.7.0_80
# -rw-rw-r--  1    1000    1000 153530841 Feb 10  2018 jdk1.7.0_80-x64.tar.gz
# -rw-rw-r--  1    1000    1000 189815615 Mar  5  2018 jdk1.8.0_1062-x64.tar.gz
# drwxrwxrwx  8 uucp        143      4096 Jun 17  2014 jdk1.8.0_11-x64
# drwxr-xr-x  8 uucp        143      4096 Mar 15  2017 jdk1.8.0_131
# drwxrwxrwx 17 root    root        12288 Jan 19  2018 lib
# -rwxrwxrwx  1 root    root          199 Jun  7  2012 link
# -rwxrwxrwx  1 root    root          234 Jun  7  2012 set_env
# -rwxrwxrwx  1 zdeploy es_user       440 Feb 25  2014 set_env.cmd
# -rwxrwxrwx  1 zdeploy es_user       369 Feb 25  2014 set_env_jdk7.cmd
# drwxrwxrwx 10 root    root         4096 Dec 12  2011 zookeeper-3.3.2
def extractJdkVersion(inputStream):
    if(inputStream != ''):
        ind = inputStream.rfind("->")
        res = ""
        if(ind >= 0):
            strLib = inputStream[ind + 3 : len(inputStream) - 1]
            indJdk = strLib.find('jdk')
            if(indJdk >= 0):
                res = strLib[indJdk + 3 : ]
        return res
    return ''

def extractJdkVersionV2(inputStream):
    if(inputStream != ''):
        while('  ' in inputStream):
            inputStream = inputStream.replace('  ', ' ')
        arrEle = inputStream.split('\n')
        listJdk = []
        for ele in arrEle:
            arrParams = ele.split(' ')
            if(len(arrParams) == 9):
                strJdk = arrParams[8]
                if('jdk' in strJdk and '-x64' in strJdk):
                    strJdk = strJdk.replace('jdk', '')
                    strJdk = strJdk.replace('.tar.gz', '')
                    listJdk.append(strJdk)
        verF = 0
        jdkRes = ''
        for jdk in listJdk:
            ver = re.search('_(.+?)-', jdk)
            if ver:
                verNumber = int(ver.group(1))
                if(verNumber > verF):
                    verF = verNumber
                    jdkRes = jdk
        return jdkRes
    return ''

# parse jzcommon
def extractJzcommon(inputStream):
    if(inputStream != ''):
        arrLib = inputStream.split('\n')
        arrLibJz = []
        for item in arrLib:
            if("libJZCommonX" in item):
                item = item.replace("libJZCommonX", "")
                item = item.replace("-", "")
                item = item.replace(".so", "")
                arrLibJz.append(item)
        return max(arrLibJz)
    return ''

    #print(arrLib)

# parse memcache
def extractMemcache(inputStream):
    if(inputStream != ''):
        return 'Ready'
    return 'Not ready'

# parse resize
def extractResize(inputStream):
    if(inputStream != ''):
        return 'Ready'
    return 'Not ready'
# parse os
def extractOsname(inputStream):
    osname = ''
    osversion = 0
    if(inputStream != ''):
        arrEle = inputStream.split('\n')
        for ele in arrEle:
            arrParams = ele.split(':\t')
            if(arrParams[0] == 'Distributor ID'):
                osname =arrParams[1].lower()
            elif(arrParams[0] == 'Release'):
                osversion = arrParams[1].split('.')[0]
        return osname + ' ' + osversion
    return ''

# parse os v2
def extractOsnameV2(inputStream):
    osname = ''
    osversion = '0'
    if(inputStream != ''):
        osfirst = extractOsnameV2Os6(inputStream)
        if(osfirst != ''):
            return osfirst
        arrEle = inputStream.split('\n')
        for ele in arrEle:
            arrParams = ele.split('=')
            if(arrParams[0].lower() == 'id'):
                osname = arrParams[1]
                while('"' in osname):
                    osname = osname.replace('"', '')
            elif(arrParams[0].lower() == 'version_id'):
                osStrVersion = arrParams[1]
                while('"' in osStrVersion):
                    osStrVersion = osStrVersion.replace('"', '')
                osversion = osStrVersion
        return osname + ' ' + osversion
    return ''

# parse os V2 cent6
# CentOS release 6.10 (Final)
# LSB_VERSION=base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
# CentOS release 6.10 (Final)
# CentOS release 6.10 (Final)
def extractOsnameV2Os6(inputStream):
    if(inputStream != ""):
        centos = re.search('CentOS release (.+?)\(Final\)', inputStream)
        if centos:
            version = centos.group(1).split('.')[0]
            return 'centos ' + version
    return ''

def beautifyRam(ram):
    idx = 0
    posFind = -1
    maxRun = len(cst_rams) -1
    while idx < maxRun:
        if(ram >= cst_rams[idx] and ram < cst_rams[idx + 1]):
            if(ram == cst_rams[idx]):
                posFind = idx
            else:
                aver = (cst_rams[idx] + cst_rams[idx+1])/2
                if(ram >= aver):
                    posFind = idx + 1
                else:
                    posFind = idx
            break
        idx = idx + 1
    if(posFind >= 0 and posFind < maxRun):
        return cst_rams[posFind]
    if(ram == cst_rams[maxRun - 1]):
        return cst_rams[maxRun - 1]
    return ram

# export object excel file (list cac dictionary)
# object {'ip', 'memcache: ', 'resize: ', 'cpu: ', 'ram : ', 'os : ', 'jdk.version : ', 'jzcommonx.version : ', 'description : '}
def exportDataToExcel(pathFileExcel, objData):
    workbook = xlsxwriter.Workbook(pathFileExcel)
    worksheet = workbook.add_worksheet()

    # write header
    worksheet.write_string('A1', 'IP')
    worksheet.write_string('B1', 'Memcache')
    worksheet.write_string('C1', 'Resize')
    worksheet.write_string('D1', 'CPU')
    worksheet.write_string('E1', 'RAM')
    worksheet.write_string('F1', 'OS')
    worksheet.write_string('G1', 'jdk.version')
    worksheet.write_string('H1', 'jzcommonx.version')
    worksheet.write_string('I1', 'Description')

    # write_data
    row = 1
    col = 0
    for server in objData :
        worksheet.write_string(row, col, server['ip'])
        worksheet.write_string(row, col + 1, server['memcache'])
        worksheet.write_string(row, col + 2, server['resize'])
        worksheet.write_number(row, col + 3, server['cpu'])
        worksheet.write_number(row, col + 4, server['ram'])
        worksheet.write_string(row, col + 5, server['os'])
        worksheet.write_string(row, col + 6, server['jdk'])
        worksheet.write_string(row, col + 7, server['jzcommon'])
        worksheet.write_string(row, col + 8, server['description'])
        row += 1
    workbook.close()

# get data list server
def getListServerInfo(listIp):
    listServer = []
    for ip in listIp:
        server = {}
        server['ip'] = ip

        sshClient = getConnection(ip)

        if(sshClient is not None):
            # get os name
            osStream = getOutputStreamFromServer(sshClient, 'lsb_release -a')
            server['os'] = extractOsname(osStream)
            if(server['os'] == ''):
                osStream = getOutputStreamFromServer(sshClient, 'cat /etc/*-release')
                server['os'] = extractOsnameV2(osStream)

            # get cpu
            cpuStream = getOutputStreamFromServer(sshClient, "grep -m 1 'cpu cores' /proc/cpuinfo")
            server['cpu'] = extractCpuInfo(cpuStream)

            # get ram
            ramStream = getOutputStreamFromServer(sshClient, "grep -m 1 'MemTotal' /proc/meminfo")
            server['ram'] = extractMemInfo(ramStream)

            # get jdk.version
            jdkStream = getOutputStreamFromServer(sshClient, "ls -l /zserver/java")
            server['jdk'] = extractJdkVersionV2(jdkStream)

            # get jzcommon
            jzcommonStream = getOutputStreamFromServer(sshClient, "ls  /zserver/lib/zx")
            server['jzcommon'] = extractJzcommon(jzcommonStream)

            # get memcache
            memcacheStream = getOutputStreamFromServer(sshClient, "ls -l /zserver/memcached")
            server['memcache'] = extractMemcache(memcacheStream)
            # get resize
            resizeStream = getOutputStreamFromServer(sshClient, "ls -l /zserver/lib/libJMagick.so")
            server['resize'] = extractResize(resizeStream)

            server['description'] = ''
            print(server)
            listServer.append(server)
            sshClient.close()
    return listServer

# extractCpuInfo('b\'cpu cores\t: 4\n\'')

# extractJdkVersion("b'lrwxrwxrwx 1 root root 30 Th05 14 14:42 /zserver/java/jdk -> /zserver/java/jdk1.8.0_162-x64\n'")

# extractJzcommon("b'libjemalloc-4.2.0.so\nlibjffi-1.2.so\nlibJZCommonX-1.2.0.2.so\nlibJZCommonX-1.2.0.3.so\nlibJZCommonX-1.2.0.4.so\nlibJZCommonX-1.2.0.5.so\nlibJZCommonX-1.2.0.6.so\nlibJZCommonX-1.2.0.7.so\nlibJZCommonX-1.2.0.8.so\nlibJZCommonX-1.2.0.9.so\nlibJZCommonX-1.2.1.0.so\nlibJZCommonX-1.2.1.1.so\nlibJZCommonX-1.2.1.2.so\nlibJZCommonX-1.2.1.3.so\nlibJZCommonX-1.2.1.4.so\nlibKcdbX-1.0.0.0.so\nlibZCommonX-1.2.0.3.so\nlibZCommonX-1.2.0.4.so\nlibZCommonX-1.2.0.5.so\nlibZCommonX-1.2.0.6.so\nlibZCommonX-1.2.0.7.so\nlibZCommonX-1.2.0.8.so\nlibZCommonX-1.2.0.9.so\nlibZCommonX-1.2.1.0.so\nlibZCommonX-1.2.1.1.so\nlibZCommonX-1.2.1.2.so\nlibZCommonX-1.2.1.3.so\nlibZCommonX-1.2.1.4.so\nlibZiCacheX-1.2.0.3.so\nlibZiCacheX-1.2.0.4.so\nlibZiCacheX-1.2.0.5.so\nlibZiCacheX-1.2.0.6.so\nlibZiCacheX-1.2.0.7.so\nlibZiCacheX-1.2.0.8.so\n'")

# extractOsname("b'DISTRIB_ID=Ubuntu\nDISTRIB_RELEASE=16.04\nDISTRIB_CODENAME=xenial\nDISTRIB_DESCRIPTION=\"Ubuntu 16.04.5 LTS\"\nNAME=\"Ubuntu\"\nVERSION=\"16.04.5 LTS (Xenial Xerus)\"\nID=ubuntu\nID_LIKE=debian\nPRETTY_NAME=\"Ubuntu 16.04.5 LTS\"\nVERSION_ID=\"16.04\"\nHOME_URL=\"http://www.ubuntu.com/\"\nSUPPORT_URL=\"http://help.ubuntu.com/\"\nBUG_REPORT_URL=\"http://bugs.launchpad.net/ubuntu/\"\nVERSION_CODENAME=xenial\nUBUNTU_CODENAME=xenial\n'")

# osstream = '''
# CentOS Linux release 7.4.1708 (Core)
# NAME="CentOS Linux"
# VERSION="7 (Core)"
# ID="centos"
# ID_LIKE="rhel fedora"
# VERSION_ID="7"
# PRETTY_NAME="CentOS Linux 7 (Core)"
# ANSI_COLOR="0;31"
# CPE_NAME="cpe:/o:centos:centos:7"
# HOME_URL="https://www.centos.org/"
# BUG_REPORT_URL="https://bugs.centos.org/"

# CENTOS_MANTISBT_PROJECT="CentOS-7"
# CENTOS_MANTISBT_PROJECT_VERSION="7"
# REDHAT_SUPPORT_PRODUCT="centos"
# REDHAT_SUPPORT_PRODUCT_VERSION="7"

# CentOS Linux release 7.4.1708 (Core)
# CentOS Linux release 7.4.1708 (Core)
# '''
# osstream = '''
# CentOS release 6.10 (Final)
# LSB_VERSION=base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch
# CentOS release 6.10 (Final)
# CentOS release 6.10 (Final)
# '''

#print(extractOsnameV2(osstring))

# jdkstring = '''
# total 335388
# drwxrwxrwx  9 root    root         4096 Mar  2  2011 apache-activemq-5.4.2
# drwxrwxrwx  6 root    root         4096 Dec 20  2010 apache-ant-1.8.2
# drwxrwxrwx 10 root    root         4096 Dec 12  2011 apache-tomcat-7.0.8
# drwxrwxrwx  9 root    root         4096 Mar  2  2011 axis2-1.5.4
# lrwxrwxrwx  1 root    root           11 Nov 17  2015 jdk -> jdk1.6.0_31
# lrwxrwxrwx  1 root    root           11 Nov 17  2015 jdk1.6.0 -> jdk1.6.0_31
# drwxrwxrwx  9 zdeploy es_user      4096 Nov 17  2015 jdk1.6.0_31
# drwxrwxrwx 10 zdeploy es_user      4096 Jun 27  2011 jdk1.7.0
# drwxrwxrwx  8 zdeploy es_user      4096 Oct  8  2013 jdk1.7.0_45-x64
# drwxrwxrwx  8 zdeploy es_user      4096 Dec 19  2013 jdk1.7.0_51-x64
# drwxr-xr-x  8 uucp        143      4096 Apr 11  2015 jdk1.7.0_80
# -rw-rw-r--  1    1000    1000 153530841 Feb 10  2018 jdk1.7.0_80-x64.tar.gz
# -rw-rw-r--  1    1000    1000 189815615 Mar  5  2018 jdk1.8.0_1062-x64.tar.gz
# drwxrwxrwx  8 uucp        143      4096 Jun 17  2014 jdk1.8.0_11-x64
# drwxr-xr-x  8 uucp        143      4096 Mar 15  2017 jdk1.8.0_131
# drwxrwxrwx 17 root    root        12288 Jan 19  2018 lib
# -rwxrwxrwx  1 root    root          199 Jun  7  2012 link
# -rwxrwxrwx  1 root    root          234 Jun  7  2012 set_env
# -rwxrwxrwx  1 zdeploy es_user       440 Feb 25  2014 set_env.cmd
# -rwxrwxrwx  1 zdeploy es_user       369 Feb 25  2014 set_env_jdk7.cmd
# drwxrwxrwx 10 root    root         4096 Dec 12  2011 zookeeper-3.3.2
# '''

# main:
listIp = getListIp('list_ip')
listServer = getListServerInfo(listIp)
exportDataToExcel('server_data.xlsx', listServer)