Exemplo n.º 1
0
 def check_consistency_warehouses(self):
     warehouses = self.instance.get_warehouses()
     flows = TupList(self.solution.data["flows"])
     flow_in = (
         flows.vfilter(lambda v: v["destination"] in warehouses)
         .take(["destination", "day", "product", "flow"])
         .to_dict(result_col=3, is_list=True)
         .vapply(lambda v: sum(v))
     )
     flow_out = (
         flows.vfilter(lambda v: v["origin"] in warehouses)
         .take(["origin", "day", "product", "flow"])
         .to_dict(result_col=3, is_list=True)
         .vapply(lambda v: sum(v))
     )
     return (
         flow_out.kvapply(lambda k, v: v - flow_in.get(k, 0))
         .update(flow_in.kvapply(lambda k, v: flow_out.get(k, 0) - v))
         .vfilter(lambda v: v != 0)
     )
Exemplo n.º 2
0
 def check_consistency_suppliers(self):
     suppliers = self.instance.get_suppliers()
     clients = self.instance.get_clients()
     flows = TupList(self.solution.data["flows"])
     sent = (
         flows.vfilter(lambda v: v["origin"] in suppliers)
         .take(["day", "product", "flow"])
         .to_dict(result_col=2, is_list=True)
         .vapply(lambda v: sum(v))
     )
     received = (
         flows.vfilter(lambda v: v["destination"] in clients)
         .take(["day", "product", "flow"])
         .to_dict(result_col=2, is_list=True)
         .vapply(lambda v: sum(v))
     )
     return (
         received.kvapply(lambda k, v: v - sent.get(k, 0))
         .update(sent.kvapply(lambda k, v: received.get(k, 0) - v))
         .vfilter(lambda v: v != 0)
     )
Exemplo n.º 3
0
class Instance(InstanceCore):
    schema = load_json(
        os.path.join(os.path.dirname(__file__), "../schemas/instance.json")
    )

    def __init__(self, data: dict):
        super().__init__(data)

        # Stores a list of the starting date of each week ordered
        self.weeks = TupList()

        # First object stores a list of the dates ordered,
        # the second the properties for each date.
        self.dates = TupList()
        self.dates_properties = SuperDict()

        # First object stores a list of the time slots ordered,
        # the second the properties for each one.
        self.time_slots = TupList()
        self.time_slots_properties = SuperDict()

        self.cache_properties()

    @classmethod
    def from_dict(cls, data: dict) -> "Instance":
        tables = ["employees", "shifts", "contracts"]

        data_p = {el: {v["id"]: v for v in data[el]} for el in tables}

        data_p["demand"] = {(el["day"], el["hour"]): el for el in data["demand"]}

        data_p["parameters"] = pickle.loads(pickle.dumps(data["parameters"], -1))

        if data.get("skill_demand"):
            data_p["skill_demand"] = {
                (el["day"], el["hour"], el["id_skill"]): el for el in data["skill_demand"]
            }
        else:
            data_p["skill_demand"] = {}

        if data.get("skills"):
            data_p["skills"] = {
                el["id"]: el for el in data["skills"]
            }
        else:
            data_p["skills"] = {}

        if data.get("skills_employees"):
            data_p["skills_employees"] = TupList(data["skills_employees"]).to_dict(
                result_col=["id_employee"], is_list=True, indices=["id_skill"]
            )
        else:
            data_p["skills_employees"] = TupList()

        return cls(data_p)

    def to_dict(self) -> Dict:
        tables = ["employees", "shifts", "contracts", "demand", "skill_demand", "skills"]

        data_p = {el: self.data[el].values_l() for el in tables}

        data_p["parameters"] = self.data["parameters"]
        data_p["skills_employees"] = [
            dict(id_employee=id_employee, id_skill=id_skill)
            for id_skill in self.data["skills_employees"]
            for id_employee in self.data["skills_employees"][id_skill]
        ]
        return pickle.loads(pickle.dumps(data_p, -1))

    def cache_properties(self):
        """Caches the list of weeks, dates and time slots and its associated properties"""
        self.weeks = self._get_weeks()
        self.dates = self._get_dates()
        self.dates_properties = self._get_dates_properties()
        self.time_slots = self._get_time_slots()
        self.time_slots_properties = self._get_time_slots_properties()

    def _get_weeks(self) -> TupList:
        """
        Returns a TupList with the starting date of each week in date time format
        For example: [datetime(2021, 9, 6, 0, 0, 0), datetime(2021, 9, 13, 0, 0, 0), ...]
        """
        return TupList(
            [
                get_one_date(self._get_start_date(), weeks=i)
                for i in range(0, self._get_horizon())
            ]
        ).sorted()

    def _get_dates(self) -> TupList:
        """
        Returns a TupList with the dates of the whole horizon in datetime format
        For example: [datetime(2021, 9, 6, 0, 0, 0), datetime(2021, 9, 7, 0, 0, 0), ...]
        """
        return TupList(
            [
                get_one_date(self._get_start_date(), pos, d)
                for d in range(0, self._get_opening_days())
                for pos, value in enumerate(self.weeks)
            ]
        ).sorted()

    def _get_dates_properties(self) -> SuperDict:
        """
        Returns a SuperDict with dates as key and its dict properties as a value
        For example: {datetime(2021, 9, 6, 0, 0, 0): {"string": "2021-09-06", "week": 36}, ...}
        """
        return SuperDict(
            {
                date: {
                    "string": get_date_string_from_ts(date),
                    "week": get_week_from_ts(date),
                }
                for date in self.dates
            }
        )

    def _get_date_string_from_date(self, date):
        """Returns the date string of a given date"""
        return self.dates_properties[date]["string"]

    def _get_week_from_date(self, date):
        """Returns the week number of a given date"""
        return self.dates_properties[date]["week"]

    def _get_time_slots(self) -> TupList:
        """
        Returns a TupList with the time slots of the whole horizon in datetime format
        For example: [datetime(2021, 9, 6, 7, 0, 0), datetime(2021, 9, 6, 8, 0, 0), ...]
        """
        nb_hours = self._get_ending_hour() - self._get_starting_hour()
        nb_slots = int(self._hour_to_slot(nb_hours))

        def date_hour_ts(d, s):
            return get_one_date_time(d, self._get_minutes(s))

        return TupList(
            [date_hour_ts(date, s) for date in self.dates for s in range(nb_slots)]
        ).sorted()

    def _get_time_slots_properties(self) -> SuperDict:
        """
        Returns a SuperDict with the time slots as key and their properties dict as a value
        For example: {datetime(2021, 9, 6, 7, 0, 0): {"string": "2021-09-06T07:00",
            "date": "2021-09-06", "week": 36, "hour": 7.0}, ...}
        """
        return SuperDict(
            {
                ts: {
                    "string": get_time_slot_string(ts),
                    "date": get_date_string_from_ts(ts),
                    "week": get_week_from_ts(ts),
                    "hour": get_hour_from_date_time(ts),
                }
                for ts in self.time_slots
            }
        )

    def _get_time_slot_string(self, ts):
        """Returns the time slot string of a given time slot"""
        return self.time_slots_properties[ts]["string"]

    def _get_date_string_from_ts(self, ts):
        """Returns the date string of a given time slot"""
        return self.time_slots_properties[ts]["date"]

    def _get_week_from_ts(self, ts):
        """Returns the week number of a given time slot"""
        return self.time_slots_properties[ts]["week"]

    def _get_hour_from_ts(self, ts):
        """Returns the hour of a given time slot"""
        return self.time_slots_properties[ts]["hour"]

    def _get_property(self, key, prop) -> SuperDict:
        """Returns a SuperDict with the key of a given 'table' and the prop as value"""
        return self.data[key].get_property(prop)

    def _get_employees(self, prop) -> SuperDict:
        """Returns a SuperDict with the employee id as key and the prop as value"""
        return self._get_property("employees", prop)

    def _get_contracts(self, prop) -> SuperDict:
        """Returns a SuperDict with the contract id as key and the prop as value"""
        return self._get_property("contracts", prop)

    def _get_shifts(self, prop) -> SuperDict:
        """Returns a SuperDict with the shift id as key and the prop as value"""
        return self._get_property("shifts", prop)

    def _get_horizon(self) -> int:
        """Returns the value of the horizon parameter"""
        return self.data["parameters"]["horizon"]

    def _get_start_date(self) -> datetime:
        """Returns the datetime object of the starting date"""
        return get_date_from_string(self.data["parameters"]["starting_date"])

    def _get_end_date(self) -> datetime:
        """
        Returns the last working day based on the starting day, the horizon in weeks
        and the number of days worked
        It assumes that the working days start at the end of the week.
        For example: 5 opening days, 2 week horizon and starting date of 2021-09-06
        would result in 2021-09-17 being the end date.
        """
        days = -(7 - self._get_opening_days()) - 1
        return get_one_date(
            self._get_start_date(), weeks=self._get_horizon(), days=days
        )

    def _get_skills(self) -> TupList:
        """Returns a TupList containing the id of the skills"""
        return self.data["skills"].keys_tl()

    def get_employees_by_skill(self, id_skill) -> TupList:
        """Returns a TupList with the employees that have the given skill"""
        return self.data["skills_employees"][id_skill]

    def _get_opening_days(self) -> int:
        """Returns the number of days that have to be worked each week"""
        return self.data["parameters"]["opening_days"]

    def _get_starting_hour(self) -> float:
        """Returns the first hour of the day that has to be worked."""
        return self.data["parameters"]["starting_hour"]

    def _get_ending_hour(self) -> float:
        """Returns the last hour of the day that has to be worked."""
        return self.data["parameters"]["ending_hour"]

    def _get_min_resting_hours(self) -> int:
        """Returns the number of resting hours that the employees have to have between working days"""
        return self.data["parameters"]["min_resting_hours"]

    def _get_slot_length(self) -> float:
        """Returns the length of a time slot in minutes"""
        return self.data["parameters"]["slot_length"]

    def slot_to_hour(self, slots) -> int:
        """Converts from slots to hours"""
        return int(slots * self._get_slot_length() / 60)

    def _hour_to_slot(self, hours) -> int:
        """Converts from a hours to slots"""
        return int(hours * 60 / self._get_slot_length())

    def _get_minutes(self, s) -> float:
        """Method to get the number of minutes from the start of the day given a slot"""
        return self._get_starting_hour() * 60 + s * self._get_slot_length()

    def _get_employees_contracts(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the contract id as value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 10, ...}
        """
        default_date = get_date_string_from_ts(self._get_end_date())

        contract_start = self._get_contracts("start_contract")
        contract_end = self._get_contracts("end_contract").vapply(
            lambda v: v or default_date
        )

        return SuperDict(
            {
                (self._get_week_from_date(week), e): c
                for week in self.weeks
                for c, e in self._get_contracts("id_employee").items()
                if contract_start[c]
                <= self._get_date_string_from_date(week)
                <= contract_end[c]
            }
        )

    def _get_employees_contract_hours(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the weekly hours of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 40, ...}
        """
        contract_hours = self._get_contracts("weekly_hours")
        return self._get_employees_contracts().vapply(
            lambda c: self._hour_to_slot(contract_hours[c])
        )

    def _get_employees_contract_days(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key and
        the max days worked weekly of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 5, ...}
        """
        contract_days = self._get_contracts("days_worked")
        return self._get_employees_contracts().vapply(lambda c: contract_days[c])

    def _get_employees_contract_shift(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift id as value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 1, ...}
        """
        contract_shift = self._get_contracts("id_shift")
        return self._get_employees_contracts().vapply(lambda c: contract_shift[c])

    def _get_employees_contract_starting_hour(self) -> SuperDict:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift starting hour
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 7, ...}
        """
        start = self._get_shifts("start")
        return self._get_employees_contract_shift().vapply(lambda s: start[s])

    def _get_employees_contract_ending_hour(self) -> SuperDict[Tuple[int, int], float]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the shift ending hour
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 21, ...}
        """
        end = self._get_shifts("end")
        return self._get_employees_contract_shift().vapply(lambda s: end[s])

    def _get_employee_time_slots_week(self) -> TupList:
        """
        Returns a TupList with the combinations of employees, weeks, dates and time slots
        in which the employees can work.
        For example: [("2021-09-06T07:00", 36, "2021-09-06", 1),
            ("2021-09-06T08:00", 36, "2021-09-06", 1), ...]
        """
        start = self._get_employees_contract_starting_hour()
        end = self._get_employees_contract_ending_hour()

        return TupList(
            (self._get_time_slot_string(ts), w, self._get_date_string_from_ts(ts), e)
            for ts in self.time_slots
            for (w, e) in start
            if self._get_week_from_ts(ts) == w
            and start[w, e] <= self._get_hour_from_ts(ts) < end[w, e]
        )

    def get_employees_time_slots_week(self) -> SuperDict:
        """
        Returns a SuperDict with the week and employee tuple as key
        and a list of time slots as value
        For example: {(36, 1): ["2021-09-06T07:00", "2021-09-06T08:00", ...], ...}
        """
        return self._get_employee_time_slots_week().take([0, 1, 3]).to_dict(0)

    def get_employees_time_slots_day(self) -> SuperDict:
        """
        Returns a SuperDict with the date and employee tuple as key
        and a list of time slots as value
        For example: {("2021-09-06", 1): ["2021-09-06T07:00", "2021-09-06T08:00", ...], ...}
        """
        return self._get_employee_time_slots_week().take([0, 2, 3]).to_dict(0)

    def get_consecutive_time_slots_employee(self) -> TupList:
        """
        Returns a TupList with a time slot, the nex time slot in the same day
        and an employee according to the employee availability
        For example: [("2021-09-06T07:00", "2021-09-06T08:00", 1), ...]
        """
        return TupList(
            [
                (ts, ts2, e)
                for (d, e), _time_slots in self.get_employees_time_slots_day().items()
                for ts, ts2 in zip(_time_slots, _time_slots[1:])
            ]
        )

    def get_opening_time_slots_set(self) -> set:
        """
        Returns a TupList with the time slots strings
        For example: ["2021-09-06T07:00", "2021-09-06T08:00", ...]
        """
        return self.time_slots.vapply(lambda v: self._get_time_slot_string(v)).to_set()

    def get_employees_ts_availability(self) -> TupList[Tuple[str, int]]:
        """
        Returns a TupList with the combinations of employees and time slots
        in which the employees can work.
        For example: [("2021-09-06T07:00", 1), ("2021-09-06T07:00", 2), ...]
        """
        return self._get_employee_time_slots_week().take([0, 3])

    def get_max_working_slots_week(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key
        and the weekly hours of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 40, ...}
        """
        return self._get_employees_contract_hours()

    def get_max_working_slots_day(self) -> SuperDict[Tuple[str, int], float]:
        """
        Returns a SuperDict with the dates and employees tuple as a key
        and the maximum slots that can be worked on a day as value
        For example: {("2021-09-06", 1): 9, ("2021-09-06", 2): 9, ("2021-09-06", 3): 8, ...}
        """
        # TODO: set up a hard limit from the parameters of the instance, set up as optional,
        #  if there is value it should be that one, if not the current calculation

        return SuperDict(
            {
                (self._get_date_string_from_date(d), e): hours
                / self._get_employees_contract_days()[w, e]
                + 1
                for d in self.dates
                for (w, e), hours in self._get_employees_contract_hours().items()
                if self._get_week_from_date(d) == w
            }
        )

    def get_min_working_slots_day(self) -> SuperDict[Tuple[str, int], int]:
        """
        Returns a SuperDict with the dates and employees tuple as a key and
        the minimum amount of slots that need to be worked each day
        For example: {("2021-09-06", 1): 4, ("2021-09-06", 2): 4, ("2021-09-06", 3): 4, ...}
        """
        return SuperDict(
            {
                (self._get_date_string_from_date(d), e): self._hour_to_slot(
                    self.data["parameters"]["min_working_hours"]
                )
                for d in self.dates
                for e in self._get_employees("id")
            }
        )

    def get_first_time_slot_day_employee(self) -> SuperDict[Tuple[str, int], str]:
        """
        Returns a SuperDict with the date and employee tuple as a key
        and the first time slot that the employee can work in the day as the value
        For example: {("2021-09-06", 1): "2021-09-06T07:00", ("2021-09-06", 2): "2021-09-06T13:00", ...}
        """
        return (
            self._get_employee_time_slots_week()
            .take([0, 2, 3])
            .to_dict(0)
            .vapply(lambda v: min(v))
        )

    def get_max_working_days(self) -> SuperDict[Tuple[int, int], int]:
        """
        Returns a SuperDict with the week and employee tuple as key and
        the max days worked weekly of the contract as the value
        Contracts are supposed to start on Monday and end on Sunday
        For example: {(36, 1): 5, ...}
        """
        return self._get_employees_contract_days()

    def _get_incompatible_slots(self) -> TupList:
        """
        Returns a TupList with tuples that have time slots in consecutive days where
        if the employee works in one time slot it can not work
        in the other based on the minimum resting time
        For example: [("2021-09-06T20:00", "2021-09-07T07:00"),
            ("2021-09-06T21:00", "2021-09-07T07:00"), ...]
        """
        if (
            24 - (self._get_ending_hour() - self._get_starting_hour())
            >= self._get_min_resting_hours()
        ):
            return TupList()

        nb_incompatible = self._hour_to_slot(
            int(
                self._get_min_resting_hours()
                - (24 - (self._get_ending_hour() - self._get_starting_hour()))
            )
        )

        time_slots_wo_last_day = self.time_slots.vfilter(
            lambda v: self._get_date_string_from_ts(v)
            != get_date_string_from_ts(self._get_end_date())
        )

        def check_same_day(ts, ts2):
            return ts.date() == ts2.date()

        def check_one_day_apart(ts, ts2):
            return (ts2 - ts).days <= 1

        return (
            TupList(
                [
                    (val, self.time_slots[pos + i])
                    for pos, val in enumerate(time_slots_wo_last_day)
                    for i in range(1, nb_incompatible + 1)
                ]
            )
            .vfilter(lambda v: not check_same_day(v[0], v[1]))
            .vfilter(lambda v: check_one_day_apart(v[0], v[1]))
            .vapply(
                lambda v: (
                    self._get_time_slot_string(v[0]),
                    self._get_time_slot_string(v[1]),
                )
            )
        )

    def get_incompatible_slots_employee(self) -> TupList:
        """
        Returns a TupList with the incompatible time slots for each employee
        taking into account if the employee can work in both of them
        For example: [("2021-09-06T20:00", "2021-09-07T07:00", 1),
            ("2021-09-06T21:00", "2021-09-07T07:00", 1), ...]
        """
        availability = self.get_employees_ts_availability()
        return TupList(
            [
                (ts, ts2, e)
                for (ts, ts2) in self._get_incompatible_slots()
                for e in self._get_employees("name")
                if (ts, e) in availability and (ts2, e) in availability
            ]
        )

    def get_employees_managers(self) -> TupList[int]:
        """Returns the list of employees ids that are managers"""
        return self._get_employees("manager").vfilter(lambda v: v).keys_tl()

    def _filter_demand(self, ts) -> float:
        """
        Given a time slot (date time) it returns the demand, if exists, zero otherwise
        """
        return self._get_property("demand", "demand").get(
            (self._get_date_string_from_ts(ts), self._get_hour_from_ts(ts)), 0
        )

    def get_demand(self) -> SuperDict:
        """
        Returns a SuperDict indexed by the time slot (string) and the demand as value
        For example: {"2021-09-06T07:00": 10, "2021-09-06T08:00": 15, ...}
        """
        return SuperDict(
            {
                self._get_time_slot_string(ts): self._filter_demand(ts)
                for ts in self.time_slots
            }
        )

    def _filter_skills_demand(self, ts, id_skill) -> int:
        """
        Given a time slot (date time) and the id of a skill, returns the skill demand if it exists, zero otherwise
        """
        return self._get_property("skill_demand", "demand").get(
            (self._get_date_string_from_ts(ts), self._get_hour_from_ts(ts), id_skill), 0
        )

    def _employee_available(self, ts, id_employee) -> bool:
        """
        Returns a boolean indicating if the employee is available on the time slot or not
        """
        return (self._get_time_slot_string(ts), id_employee) in self.get_employees_ts_availability()

    def get_ts_demand_employees_skill(self) -> TupList:
        """
        Returns a TupList with the combinations of:
         - Time slots
         - Skill
         - Demand for the given skill on this time slot
         - Employees that master the skill and are available on the timeslot
        For example: [("2021-09-06T07:00", 1, 1, [2, 3]), ("2021-09-06T08:00", 2, 1, [1, 2]), ...]
        """
        return TupList([
            (
                self._get_time_slot_string(ts),
                id_skill,
                self._filter_skills_demand(ts, id_skill),
                self.get_employees_by_skill(id_skill).vfilter(lambda e: self._employee_available(ts, e))
            )
            for ts in self.time_slots
            for id_skill in self._get_skills()
        ])
class GenerationTests(unittest.TestCase):
    def setUp(self):
        super().setUp()

        self.full_inst_path = self._get_path("./data/instance.json")
        self.full_inst = SuperDict.from_dict(
            self.import_schema(self.full_inst_path))
        # Removing parameter tables
        self.full_inst["properties"] = self.full_inst["properties"].vfilter(
            lambda v: v["type"] == "array")
        self.one_tab_inst_path = self._get_path("./data/one_table.json")
        self.one_tab_inst = SuperDict.from_dict(
            self.import_schema(self.one_tab_inst_path))
        self.app_name = "test"
        self.second_app_name = "test_sec"
        self.default_output_path = self._get_path("./data/output")
        self.other_output_path = self._get_path("./data/output_path")
        self.last_path = self.default_output_path
        self.all_methods = TupList(
            ["getOne", "getAll", "deleteOne", "deleteAll", "update", "post"])

    def tearDown(self):
        if os.path.isdir(self.last_path):
            shutil.rmtree(self.last_path)

    @staticmethod
    def _get_path(rel_path):
        return os.path.join(path_to_tests, rel_path)

    @staticmethod
    def import_schema(path):
        with open(path, "r") as fd:
            schema = json.load(fd)
        return schema

    def test_base(self):
        runner = CliRunner()
        result = runner.invoke(
            generate_from_schema,
            [
                "-p",
                self.full_inst_path,
                "-a",
                self.app_name,
                "-o",
                self.other_output_path,
            ],
        )

        self.assertEqual(result.exit_code, 0)
        self.last_path = self.other_output_path
        self.check(output_path=self.other_output_path)

    def test_one_table_schema(self):
        runner = CliRunner()
        result = runner.invoke(
            generate_from_schema,
            [
                "-p",
                self.one_tab_inst_path,
                "-a",
                self.app_name,
                "-o",
                self.other_output_path,
            ],
        )

        self.assertEqual(result.exit_code, 0)

        instance = SuperDict.from_dict(
            {"properties": {
                "data": self.one_tab_inst
            }})
        self.last_path = self.other_output_path
        self.check(instance=instance, output_path=self.other_output_path)

    def test_one_table_one_option(self):
        runner = CliRunner()
        result = runner.invoke(
            generate_from_schema,
            [
                "-p",
                self.one_tab_inst_path,
                "-a",
                self.app_name,
                "--one",
                "newname",
                "-o",
                self.other_output_path,
            ],
        )

        self.assertEqual(result.exit_code, 0)

        instance = SuperDict.from_dict(
            {"properties": {
                "newname": self.one_tab_inst
            }})
        self.last_path = self.other_output_path
        self.check(instance=instance, output_path=self.other_output_path)

    def test_remove_method(self):
        runner = CliRunner()
        result = runner.invoke(
            generate_from_schema,
            [
                "-p",
                self.full_inst_path,
                "-a",
                self.second_app_name,
                "-o",
                self.other_output_path,
                "-r",
                "delete_detail",
                "-r",
                "put_detail",
                "-r",
                "get_detail",
                "-r",
                "patch_detail",
            ],
        )

        self.assertEqual(result.exit_code, 0)

        include_methods = self.all_methods.vfilter(
            lambda v: v not in ["deleteOne", "update", "getOne"])
        self.last_path = self.other_output_path
        self.check(
            output_path=self.other_output_path,
            include_methods=include_methods,
            app_name=self.second_app_name,
        )

    def check(self,
              instance=None,
              output_path=None,
              include_methods=None,
              app_name=None):
        if app_name is None:
            app_name = self.app_name
        db = SQLAlchemy()
        instance = instance or self.full_inst
        output_path = output_path or self.default_output_path
        include_methods = include_methods or self.all_methods
        models_dir = os.path.join(output_path, "models")
        endpoints_dir = os.path.join(output_path, "endpoints")
        schemas_dir = os.path.join(output_path, "schemas")
        created_dirs = [output_path, models_dir, endpoints_dir, schemas_dir]

        # Checks that the directories have been created
        for path in created_dirs:
            self.assertTrue(os.path.isdir(self._get_path(path)))

        # Checks that each file has been created
        created_dirs = created_dirs[1:4]
        files = (instance["properties"].keys_tl().vapply(
            lambda v: (app_name + "_" + v + ".py", v)))
        absolute_paths = [
            os.path.join(path, file) for path in created_dirs
            for file, _ in files
        ]
        for path_file in absolute_paths:
            self.assertTrue(os.path.exists(self._get_path(path_file)))
            if os.path.exists(path_file):
                with open(path_file, "r") as fd:
                    txt = fd.read()
                packages_to_mock = [
                    "..shared.utils",
                    ".meta_model",
                    ".meta_resource",
                    "..shared.const",
                    "..shared.authentification",
                    "..models",
                    "..schemas",
                ]
                for package in packages_to_mock:
                    txt = txt.replace(package, "mockedpackage")

                with open(path_file, "w") as fd:
                    fd.write(txt)

        # Checks that the models have the correct methods and attributes
        for file, table in files:
            class_name = self.snake_to_camel(app_name + "_" + table + "_model")
            file_path = os.path.join(models_dir, file)
            spec = importlib.util.spec_from_file_location(
                class_name, file_path)
            mod = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(mod)

            # Checks correct inheritance

            self.assertTrue(
                issubclass(mod.__dict__[class_name], TraceAttributesModel))

            # Checks that the all the columns are declared, have the correct type
            props_and_methods = mod.__dict__[class_name].__dict__
            props = dict()
            for col in props_and_methods["__table__"]._columns:
                props[col.key] = next(iter(col.proxy_set))

            expected_prop = instance["properties"][table]["items"][
                "properties"]
            for prop in expected_prop:
                self.assertIn(prop, props)
                types = expected_prop[prop]["type"]
                if isinstance(types, list):
                    types = TupList(types).vfilter(lambda v: v != "null")[0]

                type_converter = {
                    db.String: "string",
                    TEXT: "string",
                    JSON: "object",
                    Integer: "integer",
                    db.Integer: "integer",
                    db.Boolean: "boolean",
                    db.SmallInteger: "integer",
                    db.Float: "number",
                }
                actual_type = "null"
                for possible_type, repr_type in type_converter.items():
                    if isinstance(props[prop].type, possible_type):
                        actual_type = repr_type

                self.assertEqual(types, actual_type)
            # Checks that all the methods are declared
            expected_methods = ["__init__", "__repr__", "__str__"]
            expected_methods = set(expected_methods)
            for method in expected_methods:
                self.assertIn(method, props_and_methods.keys())

        # Checks that the schemas have the correct methods and attributes
        for file, table in files:
            mod_name = self.snake_to_camel(app_name + "_" + table + "_schema")
            class_names = [
                self.snake_to_camel(app_name + "_" + table + "_" + type_schema)
                for type_schema in
                ["response", "edit_request", "post_request"]
            ]
            file_path = os.path.join(schemas_dir, file)
            spec = importlib.util.spec_from_file_location(mod_name, file_path)
            mod = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(mod)
            existing_classes = list(mod.__dict__.keys())
            # Checks that all the schemas are created
            for class_name in class_names:
                self.assertIn(class_name, existing_classes)
                props = mod.__dict__[class_name]._declared_fields
                # Checks that the classes have all the attributes
                expected_prop = instance["properties"][table]["items"][
                    "properties"]
                expected_prop = TupList(expected_prop).vfilter(
                    lambda v: v != "id")
                for prop in expected_prop:
                    self.assertIn(prop, props)

        # Checks that the endpoints have all the methods
        for file, table in files:
            mod_name = self.snake_to_camel(app_name + "_" + table +
                                           "_endpoint")
            class_names = [
                self.snake_to_camel(app_name + "_" + table + "_endpoint")
            ]
            if ("getOne" in include_methods or "deleteOne" in include_methods
                    or "update" in include_methods):
                class_names.append(
                    self.snake_to_camel(app_name + "_" + table +
                                        "_details_endpoint"))
            file_path = os.path.join(endpoints_dir, file)
            spec = importlib.util.spec_from_file_location(mod_name, file_path)
            mod = importlib.util.module_from_spec(spec)
            spec.loader.exec_module(mod)
            existing_classes = list(mod.__dict__.keys())
            # Checks that all the endpoints are created
            for class_name in class_names:
                self.assertIn(class_name, existing_classes)

            api_methods = {
                "get_detail": "GET",
                "get_list": "GET",
                "post_list": "POST",
                "delete_detail": "DELETE",
                "put_detail": "PUT",
                "patch_detail": "PATCH",
            }
            # Checks the methods of the first endpoint
            include_methods_e1 = [
                method_name for method_name in include_methods
                if method_name in ["get_list", "post_list"]
            ]
            props_and_methods = mod.__dict__[class_names[0]].methods
            for method_name in include_methods_e1:
                self.assertIn(api_methods[method_name], props_and_methods)

            # Checks the methods of the details endpoint
            if len(class_names) == 2:
                include_methods_e2 = [
                    method_name for method_name in include_methods
                    if method_name in [
                        "get_detail", "put_detail", "delete_detail",
                        "patch_detail"
                    ]
                ]
                props_and_methods = mod.__dict__[class_names[1]].methods
                for method_name in include_methods_e2:
                    self.assertIn(api_methods[method_name], props_and_methods)

    @staticmethod
    def snake_to_camel(name):
        return "".join(word.title() for word in name.split("_"))