def get_location_by_name(self, location_name):
        """
        Returns the dictionary of a location for a given name.

        - returns one of the locations if name is not unique

        - returns None if name doesn't exist

        :param location_name: Course code of a course
        :type location_name: int

        :return: containing all information about the given location
        :rtype: dict
        """

        location_list = self._request_handler.fetch(
            crmrequestfactory.get_location_list_by_location_name(location_name)
        )

        pretty_print(location_list)
        for location in location_list[RESULTS_FIELD]:
            return self._request_handler.fetch(
                crmrequestfactory.get_location(location))

        trace("COURSE NOT FOUND: [{}]".format(location_name))
        return None
    def update_student_dates(self, student_id, course_data):
        """
        The following will be copied from the course data:

        - Date of every lecture

        - Date of every dayoffs

        The following will be calculated:

        - Date descriptions. This is one string containing all of the lectures and vacations, directly insertable to
          emails.

        :param student_id: id number of the student as a string.
        :type student_id: str

        :param course_data: all of the date about a course the student has applied to as stored in the MiniCRM system.
        :type course_data: dict

        :return: None
        """
        data_to_update = {
            "Datumleirasok": self._get_date_description(course_data)
        }

        data_to_update = copy_dates_from_course_data_to_student_data(data_to_update, course_data)

        trace("DATA TO BE REPLACED:")
        pretty_print(data_to_update)

        self._request_handler.fetch(crmrequestfactory.set_project_data(student_id, data_to_update))
    def fill_student_data(self, student_data, course_data):
        """
        Updates the following data of the student:

        - Detailed description of the location (fetched from locations)

        The following will be copied from the course data:

        - Type of the course

        - Time of the lectures (during the day)

        - Date of every lecture

        - Date of every dayoffs

        The following will be calculated:

        - Application deadline determined by the following algorithm (applied in this order):

            1. by default, it is 5 days
            2. If the course starts in less than 7 days, or less than 30% of places is free, it is set to 3 days
            3. If the course starts in less than 3 days, and there is no more than 3 places, it will be 1 day
            4. If the starting day is earlier than the calculated deadline, it will be the starting day - 1 day
            5. If the deadline is earlier than now, it is now + 1 day

        - Date descriptions. This is one string containing all of the lectures and vacations, directly insertable to
          emails.

        :param student_data: all of the date about a student as stored in the MiniCRM system.
        :type student_data: dict

        :param course_data: all of the date about a course the student has applied to as stored in the MiniCRM system.
        :type course_data: dict

        :return: None
        """
        data_to_update = {
            "TanfolyamTipusa2": course_data["TanfolyamTipusa"],
            "Helyszin2": course_data["Helyszin"],
            "HelyszinReszletesLeiras": self._get_detailed_description_of_location(course_data["Helyszin"]),
            "OrakIdopontja2": course_data["OrakIdopontja"],
            "VeglegesitesiHatarido": self._get_application_deadline(course_data),

            "Datumleirasok": self._get_date_description(course_data)
        }

        data_to_update = copy_dates_from_course_data_to_student_data(data_to_update, course_data)

        trace("DATA TO BE REPLACED:")
        pretty_print(data_to_update)

        self._request_handler.fetch(crmrequestfactory.set_project_data(student_data["Id"], data_to_update))
    def update_headcounts(self):
        """
        Loops through all courses in Application Open ("Jelentkezés nyitva") state, and updates their headcount fields.

        It writes the result to the CRM page of the course.

        A student is considered enrolled to a course if their status is either "INFO Sent" ("INFO levél kiment") or
        "Course In Progress" ("Kurzus folyamatban").

        The function loops through all courses in Application Open ("Jelentkezés nyitva") state in a big loop, and for
        each course in the loop, it loops through all of the students who are registered to that course and counts how
        many of them are in any of the above mentioned stated. When it finishes the small loop, it updates the field for
        the current headcount of the currently processed course.

        After calling this method, the user can be sure that all courses are in up-to-date state.

        :return: None
        """
        open_courses = self.get_course_list_with_status(APPLICATION_OPEN_STATE)
        pretty_print(open_courses)

        for course in open_courses:

            course_data = self._get_project(course)
            course_code = course_data[COUSE_CODE_FIELD]

            trace("CALCULATE HEADCOUNT OF COURSE [" + course + "], code: [" + course_code + "]")

            students_in_current_course = self._request_handler.fetch(
                crmrequestfactory.get_student_list_by_course_code(course_code))[RESULTS_FIELD]

            acceptable_statuses = [
                int(self.get_student_status_number_by_name(INFO_SENT_STATE)),
                int(self.get_student_status_number_by_name(COURSE_IN_PROGRESS_STATE))
            ]

            trace("ACCEPTABLE STATUSES: [{}]".format(acceptable_statuses))

            count = 0
            for student in students_in_current_course:
                if students_in_current_course[student][STATUS_ID_FIELD] in acceptable_statuses:
                    count += 1
                    trace("STUDENT [{}] has status [{}], ACCEPTABLE, CURRENT HEADCOUNT: [{}]".
                          format(student, students_in_current_course[student][STATUS_ID_FIELD], count))
                else:
                    trace("STUDENT [{}] has status [{}], NOT ACCEPTABLE, CURRENT HEADCOUNT: [{}]".
                          format(student, students_in_current_course[student][STATUS_ID_FIELD], count))

            trace("END OF STUDENT LIST, UPDATING HEADCOUNT TO [{}]".format(count))

            self._request_handler.fetch(
                crmrequestfactory.set_project_data(course, {CURRENT_HEADCOUNT_FIELD: count}))
    def copy_applied_course_to_course_code(self, student_data):
        """
        Copies the "MelyikTanfolyamErdekli" field to "TanfolyamKodja".

        Code of the course ("TanfolyamKodja") will be equal to the course they applied to ("MelyikTanfolyamErdekli").
        This is because the latter one changes every time the registration form on MiniCRM is updated, but this way
        the course code is saved.

        :param student_data: all of the date about a student as stored in the MiniCRM system.
        :type student_data: dict

        :return: None
        """
        data_to_update = {
            "TanfolyamKodja": student_data["MelyikTanfolyamErdekli"]
        }

        trace("DATA TO BE REPLACED:")
        pretty_print(data_to_update)

        self._request_handler.fetch(crmrequestfactory.set_project_data(student_data["Id"], data_to_update))
    def get_course_by_course_code(self, course_code):
        """
        Returns the dictionary of a course.

        :param course_code: Course code of a course
        :type course_code: int

        :return: containing all information about the given ourse
        :rtype: dict
        """

        course_list = self._request_handler.fetch(
            crmrequestfactory.get_course_list_by_course_code(course_code)
        )

        pretty_print(course_list)
        for course in course_list[RESULTS_FIELD]:
            return self._request_handler.fetch(
                crmrequestfactory.get_course(course))

        trace("COURSE NOT FOUND: [{}]".format(course_code))
        return None
    def fetch(self, request):
        """
        Sends the API request to the CRM system, and returns the formatted JSON array in the for of a Python dict.

        :param request: the encapsulated API request
        :type request: ApiRequest

        :return: the JSON answer as Python dictionary.
        :rtype: dict
        """
        trace("COMMAND SENT TO API: {}, URL: {}".format(
            request.get_slogan(), request.get_url()))

        if request.get_method() == GET_METHOD:
            response = requests.get(request.get_url(),
                                    auth=(self._username, self._api_key))
        elif request.get_method() == PUT_METHOD:
            response = requests.put(request.get_url(),
                                    auth=(self._username, self._api_key),
                                    data=json.dumps(request.get_payload()))
        else:
            ValueError("Unsupported method type: [{}]".format(
                request.get_method()))

        if response.status_code == TOO_MANY_REQUESTS_STATUS_CODE:
            trace(
                "Response status code 429 (Too many requests), sleeping for " +
                str(ONE_MINUTE) + " seconds.")
            time.sleep(ONE_MINUTE)
            return self.fetch(request)

        trace("RAW RECEIVED: {}".format(response))
        formatted_output = response.json
        trace("ANSWER RECEIVED:")
        pretty_print(formatted_output)
        return formatted_output
 def _get_detailed_description_of_location(self, location_name):
     location_data = self.get_location_by_name(location_name)
     pretty_print(location_data)
     return location_data[DETAILED_LOCATION_DESCRIPTION_FIELD]