def __init__(self, methodName='runTest'):
     super().__init__(methodName)
     self.test_online = os.environ.get("TEST_ONLINE", "0") == "1"
     self.vehicule_list = Cars()
     self.vehicule_list.extend([
         Car("VR3UHZKX", "vid", "Peugeot"),
         Car("VXXXXX", "XXXX", "Peugeot", label="SUV 3008")
     ])
Esempio n. 2
0
    def record_to_db(file_name):
        myfile = open(file_name, 'w', encoding='cp1251')
        for n, l, f, p in zip(names_list, links, photos, list_price):
            # db_session.add(1,2,3,4)
            #
            # libs.car.add_cars(n, l, f, p)
            db_session.add(Cars(n, l, f, p))
            db_session.commit()

            try:
                line = '{}{}{}{}{}'.format(n, '\t', l, '\t', f, '\t', str(p))
                myfile.write(line + '\n')
            except:
                pass
Esempio n. 3
0
def update_trips():
    global trips, chargings, cached_layout, min_date, max_date, min_millis, max_millis, step, marks
    logger.info("update_data")
    conn = Database.get_db(update_callback=False)
    Database.add_altitude_to_db(conn)
    conn.close()
    min_date = None
    max_date = None
    if CONFIG.is_good:
        car = CONFIG.myp.vehicles_list[0]  # todo handle multiple car
        try:
            trips_by_vin = Trips.get_trips(Cars([car]))
            trips = trips_by_vin[car.vin]
            assert len(trips) > 0
            min_date = trips[0].start_at
            max_date = trips[-1].start_at
            figures.get_figures(trips[0].car)
        except (AssertionError, KeyError):
            logger.debug("No trips yet")
            figures.get_figures(Car("vin", "vid", "brand"))
        try:
            chargings = Charging.get_chargings()
            assert len(chargings) > 0
            if min_date:
                min_date = min(min_date, chargings[0]["start_at"])
                max_date = max(max_date, chargings[-1]["start_at"])
            else:
                min_date = chargings[0]["start_at"]
                max_date = chargings[-1]["start_at"]
        except AssertionError:
            logger.debug("No chargings yet")
            if min_date is None:
                return
        # update for slider
        try:
            logger.debug("min_date:%s - max_date:%s", min_date, max_date)
            min_millis = web.utils.unix_time_millis(min_date)
            max_millis = web.utils.unix_time_millis(max_date)
            step = (max_millis - min_millis) / 100
            marks = web.utils.get_marks_from_start_end(min_date, max_date)
            cached_layout = None  # force regenerate layout
            figures.get_figures(car)
        except (ValueError, IndexError):
            logger.error("update_trips (slider): %s", exc_info=True)
        except AttributeError:
            logger.debug("position table is probably empty :", exc_info=True)
    return
Esempio n. 4
0
 def __init__(self, refresh_token, client_id, client_secret, remote_refresh_token, customer_id, realm, country_code,
              proxies=None, weather_api=None, abrp=None, co2_signal_api=None):
     self.realm = realm
     self.service_information = ServiceInformation(AUTHORIZE_SERVICE,
                                                   realm_info[self.realm]['oauth_url'],
                                                   client_id,
                                                   client_secret,
                                                   SCOPE, False)
     self.client_id = client_id
     self.manager = OpenIdCredentialManager(self.service_information)
     self.api_config = Oauth2PSACCApiConfig()
     self.api_config.set_refresh_callback(self.refresh_token)
     self.manager.refresh_token = refresh_token
     self.remote_refresh_token = remote_refresh_token
     self.remote_access_token = None
     self.vehicles_list = Cars.load_cars(CARS_FILE)
     self.customer_id = customer_id
     self._config_hash = None
     self.api_config.verify_ssl = False
     self.api_config.api_key['client_id'] = self.client_id
     self.api_config.api_key['x-introspect-realm'] = self.realm
     self.headers = {
         "x-introspect-realm": realm,
         "accept": "application/hal+json",
         "User-Agent": "okhttp/4.8.0",
     }
     self.remote_token_last_update = None
     self._record_enabled = False
     self.otp = None
     self.weather_api = weather_api
     self.country_code = country_code
     self.mqtt_client = None
     self.precond_programs = {}
     self.info_callback = []
     self.info_refresh_rate = 120
     if abrp is None:
         self.abrp = Abrp()
     else:
         self.abrp: Abrp = Abrp(**abrp)
     self.set_proxies(proxies)
     self.config_file = DEFAULT_CONFIG_FILENAME
     Ecomix.co2_signal_key = co2_signal_api
Esempio n. 5
0
 def __init__(self, refresh_token, client_id, client_secret, remote_refresh_token, customer_id, realm, country_code,
              proxies=None, weather_api=None, abrp=None, co2_signal_api=None):
     self.realm = realm
     self.service_information = ServiceInformation(AUTHORIZE_SERVICE,
                                                   realm_info[self.realm]['oauth_url'],
                                                   client_id,
                                                   client_secret,
                                                   SCOPE, False)
     self.client_id = client_id
     self.manager = OpenIdCredentialManager(self.service_information)
     self.api_config = Oauth2PSACCApiConfig()
     self.api_config.set_refresh_callback(self.manager.refresh_token_now)
     self.manager.refresh_token = refresh_token
     self.account_info = AccountInformation(client_id, customer_id, realm, country_code)
     self.remote_access_token = None
     self.vehicles_list = Cars.load_cars(CARS_FILE)
     self.customer_id = customer_id
     self._config_hash = None
     self.api_config.verify_ssl = False
     self.api_config.api_key['client_id'] = self.client_id
     self.api_config.api_key['x-introspect-realm'] = self.realm
     self.remote_token_last_update = None
     self._record_enabled = False
     self.weather_api = weather_api
     self.country_code = country_code
     self.info_callback = []
     self.info_refresh_rate = 120
     if abrp is None:
         self.abrp = Abrp()
     else:
         self.abrp: Abrp = Abrp(**abrp)
     self.set_proxies(proxies)
     self.config_file = DEFAULT_CONFIG_FILENAME
     Ecomix.co2_signal_key = co2_signal_api
     self.refresh_thread = None
     remote_credentials = RemoteCredentials(remote_refresh_token)
     remote_credentials.update_callbacks.append(self.save_config)
     self.remote_client = RemoteClient(self.account_info,
                                       self.vehicles_list,
                                       self.manager,
                                       remote_credentials)
Esempio n. 6
0
    def work(self, wnum):
        self.log.debug(f'{wnum} worker started')
        rab_connection = RabbitQueue(CRAWLER_EXCHANGE_NAME, CRAWLER_QUEUE_NAME)
        db_connection = DbPg(logger=None)
        for raw_msg in rab_connection.get_generator(self.exit_event):
            if not raw_msg:
                if self.exit_event.wait(2):
                    break
                continue

            msg = raw_msg.json()
            print(msg)

            if 'url' not in msg:
                self.log.warning(f'{wnum}: bad task: {msg}')
                raw_msg.ack()
                continue
            print()
            if msg['num'] == 0:
                msg['url'] = PAGE_URL0
                print("0", msg)

            try:
                request = requests.get(msg['url'], headers=HEADERS).content
                soup = BeautifulSoup(request, 'html.parser')

                self.log.debug(msg['url'])
                time.sleep(1)

                names_list = []
                container_names = soup.select('div.information-container h2 a')
                for name in container_names:
                    str_name = name.text
                    print(str_name)
                    names_list.append(str_name)

                links = []
                container_links = soup.select('div.information-container h2 a')
                for i in container_links:
                    ii = i['href'].split("&")[0]
                    full_link = ("https://www.autotrader.co.uk" + ii)
                    link = full_link.split('?')[0]
                    links.append(link)

                photos = []
                container_photo = soup.select(
                    'figure.listing-main-image a img')
                for link_photo in container_photo:
                    photos.append(link_photo['src'])

                list_price = []
                container_text = soup.find_all(
                    "a",
                    attrs={
                        "class":
                        "js-click-handler listing-fpa-link listings-price-link tracking-standard-link"
                    })
                for i in container_text:
                    pr = i.find_all("div", attrs={"class": "vehicle-price"})
                    str_price = "".join((re.findall(r'[0-9]{,3},[0-9]{,3}',
                                                    str(pr))))
                    price = 27 * int(str_price.replace(',', ''))
                    list_price.append(price)

                for n, l, f, p in zip(names_list, links, photos, list_price):

                    db_session.add(Cars(n, l, f, p))
                    db_session.commit()

                    data = '{}{}{}{}{}'.format(n, '\t', l, '\t', f, '\t',
                                               str(p))
                    self.log.debug(data)

                db_session.add(Pages(msg['num']))
                db_session.commit()
                raw_msg.ack()
            except Exception as e0:
                self.log.exception()(
                    f'{wnum}: get page error: {e0}')  ##self.log.error
                raw_msg.nack(requeue=True)
                prox = None
                if USE_PROXY:
                    self.proxy_gen.back_proxy(prox, str(e0))

            time.sleep(random.randrange(1, 5))

        rab_connection.close()
        self.log.info(f'{wnum}: worker exit')
Esempio n. 7
0
    def get_trips(vehicles_list: Cars) -> Dict[str, "Trips"]:  # noqa: MC0001
        # pylint: disable=too-many-locals,too-many-statements,too-many-nested-blocks,too-many-branches
        conn = Database.get_db()
        vehicles = conn.execute(
            "SELECT DISTINCT vin FROM position;").fetchall()
        trips_by_vin = {}
        for vin in vehicles:
            trips = Trips()
            vin = vin[0]
            res = conn.execute(
                'SELECT Timestamp, VIN, longitude, latitude, mileage, level, moving, temperature,'
                ' level_fuel, altitude FROM position WHERE VIN=? ORDER BY Timestamp',
                (vin, )).fetchall()
            if len(res) > 1:
                car = vehicles_list.get_car_by_vin(vin)
                assert car is not None
                trip_parser = TripParser(car)
                start = res[0]
                end = res[1]
                trip = Trip()
                # for debugging use this line res = list(map(dict,res))
                for x in range(0, len(res) - 2):
                    if logger.isEnabledFor(
                            logging.DEBUG
                    ):  # reduce execution time if debug disabled
                        logger.debugv("%s mileage:%.1f level:%s level_fuel:%s",
                                      res[x]['Timestamp'], res[x]['mileage'],
                                      res[x]['level'], res[x]['level_fuel'])
                    next_el = res[x + 2]
                    distance = 0
                    try:
                        distance = end["mileage"] - start["mileage"]
                    except TypeError:
                        logger.debug("Bad mileage value in DB")
                    duration = (end["Timestamp"] -
                                start["Timestamp"]).total_seconds() / 3600
                    try:
                        speed_average = distance / duration
                    except ZeroDivisionError:
                        speed_average = 0

                    if TripParser.is_low_speed(
                            speed_average, duration) or trip_parser.is_refuel(
                                start, end, distance):
                        start = end
                        trip = Trip()
                        logger.debugv(
                            "restart trip at {0[Timestamp]} mileage:{0[mileage]:.1f} level:{0[level]}"
                            " level_fuel:{0[level_fuel]}",
                            start,
                            style='{')
                    else:
                        distance = next_el["mileage"] - end["mileage"]  # km
                        duration = (next_el["Timestamp"] -
                                    end["Timestamp"]).total_seconds() / 3600
                        try:
                            speed_average = distance / duration
                        except ZeroDivisionError:
                            speed_average = 0
                        end_trip = False
                        if trip_parser.is_refuel(end, next_el, distance) or \
                                TripParser.is_low_speed(speed_average, duration):
                            end_trip = True
                        elif duration > 2:
                            end_trip = True
                            logger.debugv("too much time detected")
                        elif x == len(res) - 3:  # last record detected
                            # think if add point is needed
                            end = next_el
                            end_trip = True
                            logger.debugv("last position found")
                        if end_trip:
                            logger.debugv(
                                "stop trip at {0[Timestamp]} mileage:{0[mileage]:.1f} level:{0[level]}"
                                " level_fuel:{0[level_fuel]}",
                                end,
                                style='{')
                            trip.distance = end["mileage"] - start[
                                "mileage"]  # km
                            if trip.distance > 0:
                                trip.start_at = start["Timestamp"]
                                trip.end_at = end["Timestamp"]
                                trip.add_points(end["latitude"],
                                                end["longitude"])
                                if end["temperature"] is not None and start[
                                        "temperature"] is not None:
                                    trip.add_temperature(end["temperature"])
                                trip.duration = (
                                    end["Timestamp"] -
                                    start["Timestamp"]).total_seconds() / 3600
                                trip.speed_average = trip.distance / trip.duration
                                diff_level, diff_level_fuel = trip_parser.get_level_consumption(
                                    start, end)
                                trip.set_altitude_diff(start["altitude"],
                                                       end["altitude"])
                                trip.car = car
                                if diff_level != 0:
                                    trip.set_consumption(diff_level)  # kw
                                if diff_level_fuel != 0:
                                    trip.set_fuel_consumption(diff_level_fuel)
                                trip.mileage = end["mileage"]
                                logger.debugv(
                                    "Trip: {0.start_at} -> {0.end_at} {0.distance:.1f}km {0.duration:.2f}h "
                                    "{0.speed_average:.0f}km/h {0.consumption:.2f}kWh "
                                    "{0.consumption_km:.2f}kWh/100km {0.consumption_fuel:.2f}L "
                                    "{0.consumption_fuel_km:.2f}L/100km {0.mileage:.1f}km",
                                    trip,
                                    style="{")
                                # filter bad value
                                trips.check_and_append(trip)
                            start = next_el
                            trip = Trip()
                        else:
                            trip.add_points(end["latitude"], end["longitude"])
                    end = next_el
                trips_by_vin[vin] = trips
        conn.close()
        return trips_by_vin
Esempio n. 8
0
import pytz

from libs.car import Car, Cars
from libs.charging import Charging
from web.db import Database

DATA_DIR = os.path.dirname(os.path.realpath(__file__)) + "/data/"
latitude = 47.2183
longitude = -1.55362
date3 = datetime.utcnow().replace(2021, 3, 1, 12, 00, 00, 00, tzinfo=pytz.UTC)
date2 = date3 - timedelta(minutes=20)
date1 = date3 - timedelta(minutes=40)
date0 = date3 - timedelta(minutes=60)
date4 = date3 + timedelta(minutes=1)

vehicule_list = Cars()
vehicule_list.extend(
    [Car("VR3UHZKX", "vid", "Peugeot"), Car("VXXXXX", "XXXX", "Peugeot", label="SUV 3008 Hybrid 225")])
car = vehicule_list[0]
DB_DIR = DATA_DIR + "tmp.db"


def get_new_test_db():
    try:
        os.remove(DATA_DIR + "tmp.db")
    except FileNotFoundError:
        pass
    Database.DEFAULT_DB_FILE = DB_DIR
    Database.db_initialized = False
    conn = Database.get_db()
    return conn
Esempio n. 9
0
    def work(self, wnum):
        self.log.debug(f'{wnum} worker started')
        rab_connection = RabbitQueue(CRAWLER_EXCHANGE_NAME, CRAWLER_QUEUE_NAME)
        db_connection = DbPg(logger=None)
        # driver, prox = self.init_browser()
        for raw_msg in rab_connection.get_generator(self.exit_event):
            if not raw_msg:
                if self.exit_event.wait(2):
                    break
                continue

            msg = raw_msg.json()
            print(msg)

            if 'url' not in msg:
                self.log.warning(f'{wnum}: bad task: {msg}')
                raw_msg.ack()
                continue
            print()
            if msg['num'] == 0:
                msg['url'] = PAGE_URL0
                # msg['url'] = msg['url'].split('?')[0]
                print("0",msg)

            try:
                # driver.get(msg['url'])
                request = requests.get(msg['url'], headers=HEADERS).content
                soup = BeautifulSoup(request, 'html.parser')
                # container = soup.select("li.search-page__result")

                self.log.debug(msg['url'])
                # self.log.debug(driver.current_url)
                time.sleep(1)

                names_list = []
                container_names = soup.select('div.information-container h2 a')
                for name in container_names:
                    str_name = name.text
                    #name = str_name.strip()
                    print(str_name)
                    names_list.append(str_name)

                links = []
                container_links = soup.select('div.information-container h2 a')
                for i in container_links:
                    ii = i['href'].split("&")[0]
                    # ii = i['href']
                    full_link = ("https://www.autotrader.co.uk" + ii)
                    link = full_link.split('?')[0]
                    links.append(link)
                    #print(link)

                photos = []
                container_photo = soup.select('figure.listing-main-image a img')
                for link_photo in container_photo:
                    photos.append(link_photo['src'])
                    #print(link_photo['src'])

                list_price = []
                container_text = soup.find_all("a", attrs={ "class" : "js-click-handler listing-fpa-link listings-price-link tracking-standard-link"})
                for i in container_text:
                    pr = i.find_all("div", attrs={ "class" : "vehicle-price"})
                    str_price = "".join((re.findall(r'[0-9]{,3},[0-9]{,3}', str(pr))))
                    price =27*int(str_price.replace(',', ''))
                    list_price.append(price)

                for n, l, f, p in zip(names_list, links, photos, list_price):

                    db_session.add(Cars(n, l, f, p))
                    db_session.commit()

                    data = '{}{}{}{}{}'.format(n, '\t', l, '\t', f, '\t',str(p))
                                                                    # parse with selenium
                                                                    # rows = driver.find_elements_by_css_selector("tr")
                                                                    # if not rows:
                                                                    #     self.log.debug(f'{wnum}: not rows in table')
                                                                    #     raw_msg.nack(requeue=True)
                                                                    #     break
                                                                    #
                                                                    # for row in rows:
                                                                    #     cells = row.find_elements_by_css_selector("td")
                                                                    #     if not cells:
                                                                    #         continue
                                                                    #
                                                                    #     data = {
                                                                    #         'img_url': cells[0].find_element_by_css_selector(
                                                                    #             'img').get_attribute('src'),
                                                                    #         'country': cells[1].find_element_by_css_selector(
                                                                    #             'span').get_attribute('title'),
                                                                    #         'vessel_name': cells[1].text.split('\n')[0],
                                                                    #         'vessel_type': cells[1].text.split('\n')[1],
                                                                    #         'year': cells[2].text,
                                                                    #         'gt': cells[3].text,
                                                                    #         'dwt': cells[4].text,
                                                                    #         'sz': cells[5].text
                                                                    #     }
                                                                    #     vlength, vwidth = [int(v.strip()) for v in data['sz'].split('/')]
                    self.log.debug(data)


                                                    #     db_connection.insert_ship(car)
                                                    # db_connection.exec_query(f'''
                                                    #     INSERT INTO pages (page_num)
                                                    #     VALUES({msg['num']})
                                                    # ''')
                db_session.add(Pages(msg['num']))
                db_session.commit()
                raw_msg.ack()
            except Exception as e0:
                self.log.exception()(f'{wnum}: get page error: {e0}')##self.log.error
                raw_msg.nack(requeue=True)
                prox = None
                if USE_PROXY:
                    self.proxy_gen.back_proxy(prox, str(e0))
                # driver.close()
                # driver, prox = self.init_browser()
            time.sleep(random.randrange(1, 5))

        rab_connection.close()
        # db_connection.close()
        self.log.info(f'{wnum}: worker exit')
Esempio n. 10
0
 def test_car(self):
     car1 = Car("VRAAAAAAA", "1sdfdksnfk222", "Peugeot", "208", 46, 0)
     car2 = Car("VR3UHZKX", "1sdfdksnfk222", "Peugeot")
     cars = Cars([car1, car2])
     cars.save_cars(name=DATA_DIR + "test_car.json")
     Cars.load_cars(name=DATA_DIR + "test_car.json")
Esempio n. 11
0
class TestUnit(unittest.TestCase):
    def __init__(self, methodName='runTest'):
        super().__init__(methodName)
        self.test_online = os.environ.get("TEST_ONLINE", "0") == "1"
        self.vehicule_list = Cars()
        self.vehicule_list.extend([
            Car("VR3UHZKX", "vid", "Peugeot"),
            Car("VXXXXX", "XXXX", "Peugeot", label="SUV 3008")
        ])

    @staticmethod
    def get_new_test_db():
        try:
            os.remove(DATA_DIR + "tmp.db")
        except:
            pass
        Database.DEFAULT_DB_FILE = DATA_DIR + "tmp.db"
        Database.db_initialized = False
        conn = Database.get_db()
        return conn

    def test_car(self):
        car1 = Car("VRAAAAAAA", "1sdfdksnfk222", "Peugeot", "208", 46, 0)
        car2 = Car("VR3UHZKX", "1sdfdksnfk222", "Peugeot")
        cars = Cars([car1, car2])
        cars.save_cars(name=DATA_DIR + "test_car.json")
        Cars.load_cars(name=DATA_DIR + "test_car.json")

    def test_otp_config(self):
        otp_config = load_otp(filename=DATA_DIR + "otp_test.bin")
        assert otp_config is not None
        save_otp(otp_config, filename=DATA_DIR + "otp_test2.bin")

    def test_mypsacc(self):
        if self.test_online:
            myp = MyPSACC.load_config("config.json")
            myp.refresh_token()
            myp.get_vehicles()
            car = myp.vehicles_list[0]
            myp.abrp.abrp_enable_vin.add(car.vin)
            res = myp.get_vehicle_info(myp.vehicles_list[0].vin)
            myp.abrp.call(car, 22.1)
            myp.save_config()
            assert isinstance(
                get_temp(str(latitude), str(longitude), myp.weather_api),
                float)

    def test_car_model(self):
        assert CarModel.find_model_by_vin("VR3UHZKXZL").name == "e-208"
        assert CarModel.find_model_by_vin("VR3UKZKXZM").name == "e-2008"
        assert CarModel.find_model_by_vin("VXKUHZKXZL").name == "corsa-e"

    def test_c02_signal_cache(self):
        start = datetime.now() - timedelta(minutes=30)
        end = datetime.now()
        Ecomix._cache = {
            'FR': [[start - timedelta(days=1), 100],
                   [start + timedelta(minutes=1), 10],
                   [start + timedelta(minutes=2), 20],
                   [start + timedelta(minutes=3), 30]]
        }
        assert Ecomix.get_co2_from_signal_cache(start, end, "FR") == 20

    def test_c02_signal(self):
        if self.test_online:
            key = "d186c74bfbcd1da8"
            Ecomix.co2_signal_key = key
            def_country = "FR"
            Ecomix.get_data_from_co2_signal(latitude, longitude, def_country)
            res = Ecomix.get_co2_from_signal_cache(
                datetime.now() - timedelta(minutes=5), datetime.now(),
                def_country)
            assert isinstance(res, float)

    def test_charge_control(self):
        charge_control = ChargeControls()
        charge_control.file_name = "test_charge_control.json"
        charge_control.save_config(force=True)

    def test_battery_curve(self):
        from libs.car import Car
        from libs.charging import Charging
        try:
            os.remove("tmp.db")
        except:
            pass
        Database.DEFAULT_DB_FILE = "tmp.db"
        conn = Database.get_db()
        list(map(dict, conn.execute('PRAGMA database_list').fetchall()))
        vin = "VR3UHZKXZL"
        car = Car(vin, "id", "Peugeot")
        Charging.record_charging(car, "InProgress", date0, 50, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "InProgress", date1, 75, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "InProgress", date2, 85, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "InProgress", date3, 90, latitude,
                                 longitude, "FR", "slow")

        res = Database.get_battery_curve(Database.get_db(), date0, vin)
        assert len(res) == 3

    def test_sdk(self):

        res = {
            'lastPosition': {
                'type': 'Feature',
                'geometry': {
                    'type': 'Point',
                    'coordinates': [9.65457, 49.96119, 21]
                },
                'properties': {
                    'updatedAt': '2021-03-29T05:16:10Z',
                    'heading': 126,
                    'type': 'Estimated'
                }
            },
            'preconditionning': {
                'airConditioning': {
                    'updatedAt':
                    '2021-04-01T16:17:01Z',
                    'status':
                    'Disabled',
                    'programs': [{
                        'enabled': False,
                        'slot': 1,
                        'recurrence': 'Daily',
                        'start': 'PT21H40M',
                        'occurence': {
                            'day': ['Sat']
                        }
                    }]
                }
            },
            'energy': [{
                'updatedAt': '2021-02-23T22:29:03Z',
                'type': 'Fuel',
                'level': 0
            }, {
                'updatedAt': '2021-04-01T16:17:01Z',
                'type': 'Electric',
                'level': 70,
                'autonomy': 192,
                'charging': {
                    'plugged': False,
                    'status': 'Disconnected',
                    'remainingTime': 'PT0S',
                    'chargingRate': 0,
                    'chargingMode': 'No',
                    'nextDelayedTime': 'PT21H30M'
                }
            }],
            'createdAt':
            '2021-04-01T16:17:01Z',
            'battery': {
                'voltage': 99,
                'current': 0,
                'createdAt': '2021-04-01T16:17:01Z'
            },
            'kinetic': {
                'createdAt': '2021-03-29T05:16:10Z',
                'moving': False
            },
            'privacy': {
                'createdAt': '2021-04-01T16:17:01Z',
                'state': 'None'
            },
            'service': {
                'type': 'Electric',
                'updatedAt': '2021-02-23T21:10:29Z'
            },
            '_links': {
                'self': {
                    'href':
                    'https://api.groupe-psa.com/connectedcar/v4/user/vehicles/myid/status'
                },
                'vehicles': {
                    'href':
                    'https://api.groupe-psa.com/connectedcar/v4/user/vehicles/myid'
                }
            },
            'timed.odometer': {
                'createdAt': None,
                'mileage': 1107.1
            },
            'updatedAt':
            '2021-04-01T16:17:01Z'
        }
        api = ApiClient()
        status: psacc.models.status.Status = api._ApiClient__deserialize(
            res, "Status")
        geocode_res = reverse_geocode.search([
            (status.last_position.geometry.coordinates[:2])[::-1]
        ])[0]
        assert geocode_res["country_code"] == "DE"
        TestUnit.get_new_test_db()
        car = Car("XX", "vid", "Peugeot")
        car.status = status
        myp = MyPSACC.load_config(DATA_DIR + "config.json")
        myp.record_info(car)
        assert "features" in json.loads(Database.get_recorded_position())
        # electric should be first
        assert car.status.energy[0].type == 'Electric'

    def test_record_position_charging(self):
        TestUnit.get_new_test_db()
        ElecPrice.CONFIG_FILENAME = DATA_DIR + "config.ini"
        car = self.vehicule_list[0]
        Database.record_position(None, car.vin, 11, latitude, longitude - 0.05,
                                 None, date0, 40, None, False)
        Database.record_position(None, car.vin, 20, latitude, longitude, 32,
                                 date1, 35, None, False)
        Database.record_position(None, car.vin, 30, latitude, longitude, 42,
                                 date2, 30, None, False)
        Database.add_altitude_to_db(Database.get_db())
        data = json.loads(Database.get_recorded_position())
        assert data["features"][1]["geometry"]["coordinates"] == [
            float(longitude), float(latitude)
        ]
        trips = Trips.get_trips(self.vehicule_list)[car.vin]
        trip = trips[0]
        map(trip.add_temperature, [10, 13, 15])
        res = trip.get_info()
        assert compare_dict(
            res, {
                'consumption_km': 24.21052631578947,
                'start_at': date0,
                'consumption_by_temp': None,
                'positions': {
                    'lat': [latitude],
                    'long': [longitude]
                },
                'duration': 40.0,
                'speed_average': 28.5,
                'distance': 19.0,
                'mileage': 30.0,
                'altitude_diff': 2,
                'id': 1,
                'consumption': 4.6
            })

        Charging.elec_price = ElecPrice.read_config()
        start_level = 40
        end_level = 85
        Charging.record_charging(car, "InProgress", date0, start_level,
                                 latitude, longitude, None, "slow")
        Charging.record_charging(car, "InProgress", date1, 70, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "InProgress", date1, 70, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "InProgress", date2, 80, latitude,
                                 longitude, "FR", "slow")
        Charging.record_charging(car, "Stopped", date3, end_level, latitude,
                                 longitude, "FR", "slow")
        chargings = Charging.get_chargings()
        co2 = chargings[0]["co2"]
        assert isinstance(co2, float)
        assert compare_dict(chargings, [{
            'start_at': date0,
            'stop_at': date3,
            'VIN': 'VR3UHZKX',
            'start_level': 40,
            'end_level': 85,
            'co2': co2,
            'kw': 20.7,
            'price': 3.84,
            'charging_mode': 'slow'
        }])
        assert get_figures(car)
        row = {
            "start_at": date0.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
            "stop_at": date3.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
            "start_level": start_level,
            "end_level": end_level
        }
        assert get_battery_curve_fig(row, car) is not None
        assert get_altitude_fig(trip) is not None

    def test_fuel_car(self):
        TestUnit.get_new_test_db()
        ElecPrice.CONFIG_FILENAME = DATA_DIR + "config.ini"
        car = self.vehicule_list[1]
        Database.record_position(None, car.vin, 11, latitude, longitude, 22,
                                 date0, 40, 30, False)
        Database.record_position(None, car.vin, 20, latitude, longitude, 22,
                                 date1, 35, 29, False)
        Database.record_position(None, car.vin, 30, latitude, longitude, 22,
                                 date2, 30, 28, False)
        trips = Trips.get_trips(self.vehicule_list)
        res = trips[car.vin].get_trips_as_dict()
        assert compare_dict(res, [{
            'consumption_km': 5.684210526315789,
            'start_at': date0,
            'consumption_by_temp': None,
            'positions': {
                'lat': [latitude],
                'long': [longitude]
            },
            'duration': 40.0,
            'speed_average': 28.5,
            'distance': 19.0,
            'mileage': 30.0,
            'altitude_diff': 0,
            'id': 1,
            'consumption': 1.08,
            'consumption_fuel_km': 10.53
        }])

    def test_db_callback(self):
        old_dummy_value = dummy_value
        TestUnit.get_new_test_db()
        Database.set_db_callback(callback_test)
        assert old_dummy_value == dummy_value
        Database.record_position(None, "xx", 11, latitude, longitude - 0.05,
                                 None, date0, 40, None, False)
        assert old_dummy_value != dummy_value

    def test_parse_hour(self):
        expected_res = [(2, 0, 0), (3, '14', 0), (0, 0, '2')]
        assert expected_res == [
            parse_hour(h) for h in ["PT2H", "PT3H14", "PT2S"]
        ]