예제 #1
0
def refresh_token(subject, session):
    """
    :param str subject:
    :param Session session:
    """
    user = UserQuery(session) \
        .is_active() \
        .has_sub(subject) \
        .one()

    token = backend.refresh_token(user.refresh_token)

    user.access_token = token['access_token']
    user.refresh_token = token['refresh_token']
    user.token_expire = datetime \
        .fromtimestamp(token['expires_at']) \
        .replace(tzinfo=timezone.utc)
예제 #2
0
 def get_user(self, subject, session):
     """
     :param str subject:
     :param sqlalchemy.orm.Session session:
     :rtype: User
     """
     return UserQuery(session) \
         .is_active() \
         .has_sub(subject) \
         .one_or_none()
예제 #3
0
def get_soon_to_expire_tokens(session):
    """
    :param Session session:
    """
    users = UserQuery(session) \
        .is_active() \
        .should_refresh_token()

    tasks = [refresh_token.si(subject=user.sub) for user in users]

    group(*tasks).apply_async()
예제 #4
0
    def handle_request(self, request, session):
        """
        :param OnGgoReceivedWebhookRequest request:
        :param Session session:
        :rtype: bool
        """
        user = UserQuery(session) \
            .is_active() \
            .has_sub(request.sub) \
            .one_or_none()

        if user:
            start_handle_ggo_received_pipeline(request.ggo, user)
            return True
        else:
            return False
예제 #5
0
    def handle_request(self, request, session):
        """
        :param OnMeteringPointsAvailableWebhookRequest request:
        :param Session session:
        :rtype: bool
        """
        user = UserQuery(session) \
            .is_active() \
            .has_sub(request.sub) \
            .one_or_none()

        if user:
            start_import_meteringpoints_pipeline(user.sub)
            return True
        else:
            return False
예제 #6
0
def trigger_handle_ggo_received_pipeline(task, subject, begin, session):
    """
    :param celery.Task task:
    :param str subject:
    :param str begin:
    :param Session session:
    """
    __log_extra = {
        'subject': subject,
        'begin': begin,
        'pipeline': 'handle_measurement_published',
        'task': 'handle_measurement_published',
    }

    begin_dt = datetime.fromisoformat(begin)

    # Get User from database
    try:
        user = UserQuery(session) \
            .is_active() \
            .has_sub(subject) \
            .one()
    except orm.exc.NoResultFound:
        raise
    except Exception as e:
        logger.exception('Failed to load User from database, retrying...',
                         extra=__log_extra)
        raise task.retry(exc=e)

    # Get stored GGOs from AccountService
    try:
        stored_ggos = get_stored_ggos(user.access_token, begin_dt)
    except AccountServiceError as e:
        if e.status_code == 400:
            raise
        else:
            logger.exception('Failed to get GGO list, retrying...',
                             extra=__log_extra)
            raise task.retry(exc=e)
    except Exception as e:
        logger.exception('Failed to get GGO list, retrying...',
                         extra=__log_extra)
        raise task.retry(exc=e)

    # Trigger handle_ggo_received pipeline for each stored GGO
    for ggo in stored_ggos:
        start_handle_ggo_received_pipeline(ggo, user)
예제 #7
0
    def handle_request(self, request, user, session):
        """
        :param SubmitAgreementProposalRequest request:
        :param User user:
        :param Session session:
        :rtype: SubmitAgreementProposalResponse
        """
        counterpart = UserQuery(session) \
            .is_active() \
            .has_public_id(request.counterpart_id) \
            .exclude(user) \
            .one_or_none()

        if not counterpart:
            return SubmitAgreementProposalResponse(success=False)

        if request.direction == AgreementDirection.INBOUND:
            user_from = counterpart
            user_to = user
        elif request.direction == AgreementDirection.OUTBOUND:
            user_from = user
            user_to = counterpart
        else:
            raise RuntimeError('This should NOT have happened!')

        agreement = self.create_pending_agreement(
            request=request,
            user=user,
            user_from=user_from,
            user_to=user_to,
        )

        session.add(agreement)
        session.flush()

        logger.info(f'User submitted TradeAgreement proposal',
                    extra={
                        'subject': user.sub,
                        'target': counterpart.sub,
                        'agreement_id': agreement.id,
                    })

        # Send e-mail to recipient of proposal
        send_invitation_received_email(agreement)

        return SubmitAgreementProposalResponse(success=True)
예제 #8
0
    def handle_request(self, request, session):
        """
        :param OnMeasurementPublishedWebhookRequest request:
        :param Session session:
        :rtype: bool
        """
        if request.measurement.type is MeasurementType.CONSUMPTION:
            user = UserQuery(session) \
                .is_active() \
                .has_sub(request.sub) \
                .one_or_none()

            if user:
                start_handle_measurement_published_pipeline(
                    request.measurement, user)

        return True
예제 #9
0
    def handle_request(self, request, session):
        """
        :param OnMeteringPointAvailableWebhookRequest request:
        :param sqlalchemy.orm.Session session:
        :rtype: bool
        """
        user = UserQuery(session) \
            .is_active() \
            .has_sub(request.sub) \
            .one_or_none()

        # User exists?
        if user is None:
            logger.error(
                f'Can not import MeteringPoint (user not found in DB)',
                extra={
                    'subject': request.sub,
                    'gsrn': request.meteringpoint.gsrn,
                })
            return False

        # MeteringPoint already present in database?
        if self.facility_exists(request.meteringpoint.gsrn, session):
            logger.info(
                f'MeteringPoint {request.meteringpoint.gsrn} already exists in DB, skipping...',
                extra={
                    'subject': request.sub,
                    'gsrn': request.meteringpoint.gsrn,
                })
            return True

        # Insert new MeteringPoint in to DB
        facility = self.create_facility(user, request.meteringpoint)

        logger.info(f'Imported MeteringPoint with GSRN: {facility.gsrn}',
                    extra={
                        'subject': user.sub,
                        'gsrn': facility.gsrn,
                        'type': facility.facility_type,
                        'facility_id': facility.id,
                    })

        return True
예제 #10
0
def consume_back_in_time(subject, begin_from, begin_to, session):
    """
    TODO UNIQUE PER (ACCOUNT, BEGIN)

    :param str subject:
    :param str begin_from:
    :param str begin_to:
    :param Session session:
    """
    user = UserQuery(session) \
        .is_active() \
        .has_sub(subject) \
        .one()

    filters = GgoFilters(
        category=GgoCategory.STORED,
        begin_range=DateTimeRange(
            begin=datetime.fromisoformat(begin_from),
            end=datetime.fromisoformat(begin_to),
        )
    )

    response = service.get_ggo_list(
        token=user.access_token,
        request=GetGgoListRequest(
            filters=filters,
        ),
    )

    tasks = [
        handle_ggo_received.s(
            subject=user.sub,
            ggo_json=ggo_schema.dump(ggo),
            address=ggo.address,
        )
        for ggo in response.results
    ]

    if tasks:
        group(*tasks).apply_async()
예제 #11
0
def handle_measurement_published(task, subject, measurement_json, session):
    """
    :param celery.Task task:
    :param str subject:
    :param JSON measurement_json:
    :param Session session:
    """
    __log_extra = {
        'subject': subject,
        'measurement_json': str(measurement_json),
        'pipeline': 'handle_measurement_published',
        'task': 'handle_measurement_published',
    }

    measurement = measurement_schema.load(measurement_json)
    subjects = set()

    # Get User from database
    try:
        user = UserQuery(session) \
            .is_active() \
            .has_sub(subject) \
            .one()
    except orm.exc.NoResultFound:
        raise
    except Exception as e:
        logger.exception('Failed to load User from database, retrying...',
                         extra=__log_extra)
        raise task.retry(exc=e)

    # Triggers handle_ggo_received for each GGO the user has stored (to retire)
    subjects.add(subject)

    # Get inbound agreements from database
    try:
        agreements = AgreementQuery(session) \
            .is_inbound_to(user) \
            .is_limited_to_consumption() \
            .is_operating_at(measurement.begin) \
            .is_active() \
            .all()
    except Exception as e:
        logger.exception(
            'Failed to load Agreements from database, retrying...',
            extra=__log_extra)
        raise task.retry(exc=e)

    # Triggers handle_ggo_received for each GGO the outbound user
    # has stored (to transfer)
    for agreement in agreements:
        subjects.add(agreement.user_from.sub)

    # Start
    tasks = [
        trigger_handle_ggo_received_pipeline.si(
            subject=sub,
            begin=measurement.begin.isoformat(),
        ) for sub in subjects
    ]

    group(*tasks).apply_async()
def import_meteringpoints_and_insert_to_db(task, subject, session):
    """
    :param celery.Task task:
    :param str subject:
    :param Session session:
    """
    __log_extra = {
        'subject': subject,
        'pipeline': 'import_meteringpoints',
        'task': 'import_meteringpoints_and_insert_to_db',
    }

    # Get User from DB
    try:
        user = UserQuery(session) \
            .is_active() \
            .has_sub(subject) \
            .one()
    except orm.exc.NoResultFound:
        raise
    except Exception as e:
        logger.exception('Failed to load User from database, retrying...',
                         extra=__log_extra)
        raise task.retry(exc=e)

    # Import MeteringPoints from DataHubService
    try:
        response = datahub_service.get_meteringpoints(user.access_token)
    except DataHubServiceConnectionError as e:
        logger.exception(
            f'Failed to establish connection to DataHubService, retrying...',
            extra=__log_extra)
        raise task.retry(exc=e)
    except DataHubServiceError as e:
        if e.status_code == 400:
            logger.exception('Got BAD REQUEST from DataHubService',
                             extra=__log_extra)
            raise
        else:
            logger.exception('Failed to import MeteringPoints, retrying...',
                             extra=__log_extra)
            raise task.retry(exc=e)

    # Save imported MeteringPoints to database
    try:
        facilities = save_imported_meteringpoints(user, response)
    except Exception as e:
        logger.exception(
            'Failed to save imported Meteringpoints to database, retrying...',
            extra=__log_extra)
        raise task.retry(exc=e)

    # Logging
    logger.info(
        f'Imported {len(facilities)} new MeteringPoints from DataHubService',
        extra=__log_extra)

    for facility in facilities:
        logger.info(f'Imported meteringpoint with GSRN: {facility.gsrn}',
                    extra={
                        'gsrn': facility.gsrn,
                        'subject': user.sub,
                        'pipeline': 'import_meteringpoints',
                        'task': 'import_meteringpoints_and_insert_to_db',
                    })
예제 #13
0
def handle_ggo_received(task, subject, address, ggo_json, session):
    """
    :param celery.Task task:
    :param str subject:
    :param str address:
    :param JSON ggo_json:
    :param Session session:
    """
    __log_extra = {
        'subject': subject,
        'address': address,
        'ggo': str(ggo_json),
        'pipeline': 'handle_ggo_received',
        'task': 'handle_ggo_received',
    }

    ggo = ggo_schema.load(ggo_json)

    # Get User from database
    try:
        user = UserQuery(session) \
            .is_active() \
            .has_sub(subject) \
            .one()
    except orm.exc.NoResultFound:
        raise
    except Exception as e:
        logger.exception('Failed to load User from database, retrying...',
                         extra=__log_extra)
        raise task.retry(exc=e)

    # Affected subjects TODO
    # affected_subjects = controller.get_affected_subjects(user, ggo, session)
    # lock_keys = [get_lock_key(sub, ggo.begin) for sub in affected_subjects]

    lock_key = ggo.begin.strftime('%Y-%m-%d-%H-%M')

    # This lock is in place to avoid timing issues when executing multiple
    # tasks for the same account at the same time, which can cause
    # the transferred or retired amount to exceed the allowed amount
    with lock(lock_key, timeout=LOCK_TIMEOUT) as acquired:
        if not acquired:
            logger.info('Could not acquire lock(s), retrying...',
                        extra=__log_extra)
            raise task.retry()

        try:
            if not ggo_is_available(user.access_token, ggo):
                logger.info('GGO is unavailable, skipping...',
                            extra=__log_extra)
                return

            # Consume GGO
            controller.consume_ggo(user, ggo, session)

        except AccountServiceError as e:
            if e.status_code == 400:
                raise
            else:
                logger.exception('Failed to consume GGO, retrying...',
                                 extra=__log_extra)
                raise task.retry(exc=e)
        except Exception as e:
            logger.exception('Failed to consume GGO, retrying...',
                             extra=__log_extra)
            raise task.retry(exc=e)