コード例 #1
0
class ProjectService(object):

    hash_key_service = HashKeyService()
    request_service = WebRequestService()

    def get_projects_by_company(self, company_id, active_only=False):
        api_url = '{0}/company/{1}/projects'.format(
            API_URL_COI,
            self.hash_key_service.encode_key_with_environment(company_id))

        # Make the request and parse the response as json
        r = self.request_service.get(api_url)
        projects = r.json()

        if (active_only):
            filtered = []
            for project in projects:
                if (project['status'] == PROJECT_STATUS_ACTIVE):
                    filtered.append(project)
            projects = filtered

        view_models = []

        for project in projects:
            view_models.append(self._map_domain_to_view_model(project))

        return view_models

    def _map_domain_to_view_model(self, domain_model):
        return {
            'project_id': domain_model['_id'],
            'name': domain_model['name'],
            'status': domain_model['status']
        }
コード例 #2
0
 def _get_site_URL(self, user_id):
     user = User.objects.get(pk=user_id)
     if user.check_password(settings.DEFAULT_USER_PW):
         hash_key_service = HashKeyService()
         return "%semployee/signup/%s" % (
             settings.SITE_URL, hash_key_service.encode_key(user_id))
     return settings.SITE_URL
コード例 #3
0
class TimeTrackingService(object):

    hash_key_service = HashKeyService()
    request_service = WebRequestService()

    def get_all_users_submitted_work_timesheet_by_week_start_date(
            self, week_start_date):
        users = []

        api_url = '{0}?start_date={1}&end_date={1}'.format(
            API_URL_WORK_TIMESHEET, week_start_date.isoformat())

        # Make the request and parse the response as json
        r = self.request_service.get(api_url)
        all_entries = r.json()

        for entry in all_entries:
            user_descriptor = entry['employee']['personDescriptor']
            user_id = int(
                self.hash_key_service.decode_key_with_environment(
                    user_descriptor))
            users.append(user_id)

        return users

    def get_company_users_submitted_work_timesheet_by_week_range(
            self, company_id, start_week_start_date, end_week_start_date):

        week_user_timesheets = {}
        api_url = '{0}api/v1/company/{1}/work_timesheets?start_date={2}&end_date={3}'.format(
            settings.TIME_TRACKING_SERVICE_URL,
            self.hash_key_service.encode_key_with_environment(company_id),
            start_week_start_date.isoformat(), end_week_start_date.isoformat())

        r = self.request_service.get(api_url)
        if r.status_code == 404:
            return week_user_timesheets

        all_entries = r.json()
        for entry in all_entries:
            user_descriptor = entry['employee']['personDescriptor']
            user_id = self.hash_key_service.decode_key_with_environment(
                user_descriptor)
            item = copy.deepcopy(entry)
            item['user_id'] = user_id

            # Group returned timesheet by work start date
            week_start_date_str = entry['weekStartDate']
            week_start_date = datetime.strptime(
                week_start_date_str, '%Y-%m-%dT%H:%M:%S.%fZ').date()
            if week_start_date in week_user_timesheets:
                week_user_timesheets[week_start_date].append(item)
            else:
                week_user_timesheets[week_start_date] = [item]

        return week_user_timesheets
コード例 #4
0
def onboard_email(name, company_id, to_emails, id):
    try:
        company = Company.objects.get(pk=company_id)
    except Company.DoesNotExist:
        raise Http404

    hash_key_service = HashKeyService()
    link = "%semployee/signup/%s" % (URL, hash_key_service.encode_key(id))

    c = CONTENT % (name, company.name, link)
    send_mail(SUBJECT, c, FROM, to_emails, fail_silently=False)
コード例 #5
0
 def build_from_record(self, compensation_record):
     self.key_hasher = HashKeyService()
     if compensation_record:
         self.id = self.key_hasher.encode_key(compensation_record.id)
         self.effective_date = compensation_record.effective_date
         self.annual_base_salary = compensation_record.annual_base_salary
         self.hourly_rate = compensation_record.hourly_rate
         self.increase_percentage = compensation_record.increase_percentage
         self.projected_hour_per_month = compensation_record.projected_hour_per_month
         self.created_at = compensation_record.created_at
         self.updated_at = compensation_record.updated_at
         self.reason = compensation_record.reason
         self.is_current = False
コード例 #6
0
class ContratorService(object):

    hash_key_service = HashKeyService()
    request_service = WebRequestService()

    def get_contractor_by_id(self, contractor_id):
        api_url = '{0}/contractor/{1}'.format(API_URL_COI, contractor_id)

        # Make the request and parse the response as json
        r = self.request_service.get(api_url)
        entry = r.json()

        return entry
コード例 #7
0
    def _get_action_data(self):
        hash_key_service = HashKeyService()
        send_email_service = SendEmailService()
        company_emails = send_email_service.get_employer_emails_for_all_companies(
        )

        result = []

        for companyId in company_emails.keys():
            result.append({
                'descriptor':
                hash_key_service.encode_key_with_environment(companyId),
                'emailList':
                company_emails[companyId]
            })

        return result
コード例 #8
0
 def _get_or_create_group(self, group_name, company_id):
     comp_group = CompanyGroup.objects.filter(company=company_id,
                                              name=group_name)
     if not comp_group:
         # Let's create a new company_group
         new_comp_group = {'company': company_id, 'name': group_name}
         group_serializer = CompanyGroupPostSerializer(data=new_comp_group)
         if group_serializer.is_valid():
             group_serializer.save()
             hash_key_service = HashKeyService()
             return hash_key_service.decode_key(group_serializer.data['id'])
         else:
             raise Exception(
                 "Group cannot be created with the name {}".format(
                     group_name))
     else:
         return comp_group[0].id
コード例 #9
0
class TimeOffService(object):

    hash_key_service = HashKeyService()
    request_service = WebRequestService()

    def get_company_users_time_off_records_by_date_range(
            self, company_id, start_date, end_date):
        user_records = []
        api_url = '{0}api/v1/company/{1}/timeoffs?start_date={2}&end_date={3}'.format(
            settings.TIME_TRACKING_SERVICE_URL,
            self.hash_key_service.encode_key_with_environment(company_id),
            start_date.isoformat(), end_date.isoformat())

        r = self.request_service.get(api_url)
        if r.status_code == 404:
            return user_records

        r.raise_for_status()

        all_entries = r.json()
        for entry in all_entries:
            user_records.append(TimeOffRecord(entry))

        # Sort the records by user ID
        sorted_records = sorted(
            user_records,
            key=lambda record:
            (record.requestor_user_id, record.start_date_time))

        return sorted_records

    def get_company_users_time_off_record_aggregates_by_date_range(
            self, company_id, start_date, end_date):
        mappings = {}

        all_records = self.get_company_users_time_off_records_by_date_range(
            company_id, start_date, end_date)
        for record in all_records:
            if record.requestor_user_id not in mappings:
                mappings[
                    record.requestor_user_id] = EmployeeTimeOffRecordAggregate(
                        record.requestor_user_id)
            mappings[record.requestor_user_id].add_record(record)

        unsorted = mappings.values()
        return sorted(unsorted, key=lambda aggregate: aggregate.user_id)
コード例 #10
0
    def test_get_user_by_credential_successful(self):
        email = '*****@*****.**'
        credential = {
            'email': email,
            'password': '******'
        }
        response = self.client.post(
            reverse('user_by_credential'),
            json.dumps(credential),
            content_type='application/json'
        )

        hash_key_service = HashKeyService()

        response_object = json.loads(response.content)
        self.assertTrue('user_info' in response_object)
        user_info = response_object['user_info']
        self.assertEqual(type(user_info), dict)
        self.assertTrue('user_id' in user_info and user_info['user_id'])
        self.assertTrue('user_id_env_encode' in user_info and user_info['user_id_env_encode'])
        self.assertEqual(str(user_info['user_id']), hash_key_service.decode_key_with_environment(user_info['user_id_env_encode']))
        self.assertTrue('account_email' in user_info and user_info['account_email'])
        self.assertEqual(user_info['account_email'], email)
        self.assertTrue('first_name' in user_info and user_info['first_name'])
        self.assertTrue('last_name' in user_info and user_info['last_name'])
        self.assertTrue('hourly_rate' in user_info and user_info['hourly_rate'])

        self.assertTrue('company_info' in response_object)
        company_info = response_object['company_info']
        self.assertEqual(type(company_info), dict)
        self.assertTrue('company_id' in company_info and company_info['company_id'])
        self.assertTrue('company_id_env_encode' in company_info and company_info['company_id_env_encode'])
        self.assertEqual(str(company_info['company_id']), hash_key_service.decode_key_with_environment(company_info['company_id_env_encode']))

        self.assertTrue('app_features_info' in response_object)
        app_features_info = response_object['app_features_info']
        self.assertEqual(type(app_features_info), dict)
コード例 #11
0
 def __init__(self):
     self.hash_service = HashKeyService()
コード例 #12
0
class ViewTestBase(object):

    hash_key_service = HashKeyService()

    def normalize_key(self, key):
        return self.hash_key_service.encode_key(key)
コード例 #13
0
class TimePunchCard(object):
    hash_key_service = HashKeyService()
    date_time_service = DateTimeService()

    def __init__(self, punch_card_domain_model):
        if (punch_card_domain_model is None):
            raise ValueError('Must pass valid time punch card domain model.')

        # List out instance variables
        self.user_id = None
        self.user_info = None
        self.date = None
        self.start = None
        self.end = None
        self.state = None
        self.card_type = None
        self.in_progress = None
        self.system_stopped = None

        # Parse out user ID
        user_descriptor = punch_card_domain_model['employee'][
            'personDescriptor']
        self.user_id = int(
            self.hash_key_service.decode_key_with_environment(user_descriptor))

        # Parse card type
        self.card_type = punch_card_domain_model.get('recordType')

        # Parse all dates and times to objects
        self.date = self.date_time_service.parse_date_time(
            punch_card_domain_model['date'])

        start_str = punch_card_domain_model.get('start')
        if (start_str):
            self.start = self.date_time_service.parse_date_time(start_str)

        end_str = punch_card_domain_model.get('end')
        if (end_str):
            self.end = self.date_time_service.parse_date_time(end_str)

        # Parse attributes
        attributes = punch_card_domain_model.get('attributes')
        if (attributes):
            for attribute in attributes:
                # For now only cares about state
                if attribute['name'] == PUNCH_CARD_ATTRIBUTE_TYPE_STATE:
                    self.state = attribute['value']
                    break

        in_progress_str = punch_card_domain_model.get('inProgress')
        if (in_progress_str):
            self.in_progress = bool(in_progress_str)

        system_stopped_str = punch_card_domain_model.get('systemStopped')
        if (system_stopped_str):
            self.system_stopped = bool(system_stopped_str)

        # Support lasy-evaluated validation
        self._validation_issues = None

    def get_punch_card_hours(self):
        raw_hours = self.__get_raw_card_hours()
        if self.card_type == PUNCH_CARD_TYPE_BREAK_TIME:
            return -raw_hours
        return raw_hours

    def __get_raw_card_hours(self):
        if (self.start is not None and self.end is not None):
            card_hours = self.date_time_service.get_time_diff_in_hours(
                self.start, self.end, 2)
            return card_hours
        return 0.0

    def get_card_day_of_week_iso(self):
        return self.date.isoweekday() % 7

    @property
    def validation_issues(self):
        if (self._validation_issues is None):
            self._validation_issues = self._validate()

        return self._validation_issues

    def is_valid(self):
        issues = self.validation_issues
        blocking_issue = next(
            (issue for issue in issues
             if issue.level > TimeCardValidationIssue.LEVEL_WARNING), None)
        return blocking_issue is None

    def _validate(self):
        validation_issues = []

        card_hours = self.__get_raw_card_hours()

        # 1. Unclosed timecard (clocked in, but not out)
        if ((self.start is not None and self.end is None)
                or (self.in_progress)):
            validation_issues.append(
                TimeCardValidationIssue(
                    TimeCardValidationIssue.LEVEL_ERROR,
                    '[Unclosed Card] Clocked in, but not out, by midnight.'))

        # 2. Negative hours
        if (card_hours < 0.0):
            validation_issues.append(
                TimeCardValidationIssue(
                    TimeCardValidationIssue.LEVEL_ERROR,
                    '[Invalid Card Balance] Card with negative accounted hours.'
                ))

        # 3. Long working hours, such as over 10 hours work
        if (card_hours >= 10.0):
            validation_issues.append(
                TimeCardValidationIssue(
                    TimeCardValidationIssue.LEVEL_WARNING,
                    '[Unusual Card Balance] Card with more than 10 hours.'))

        if (self.system_stopped):
            validation_issues.append(
                TimeCardValidationIssue(
                    TimeCardValidationIssue.LEVEL_ERROR,
                    '[System Closed Card] Card stopped accruing hours by system. Please validate!'
                ))

        return validation_issues
コード例 #14
0
 def from_native(self, data):
     hash_key_service = HashKeyService()
     return hash_key_service.decode_key(data)
コード例 #15
0
 def to_native(self, obj):
     hash_key_service = HashKeyService()
     return hash_key_service.encode_key(obj)
コード例 #16
0
    def execute_creation(self, account_info, do_validation=True):
        result = OperationResult(account_info)
        account_result = None

        # Do validation first, and short circuit if failed
        if (do_validation):
            account_result = self.validate(account_info)
        else:
            # directly construct the result, skipping validation
            account_result = OperationResult(account_info)

        # If the account creation info is not valid to begin with
        # simply short circuit and return it
        if (account_result.has_issue()):
            return account_result

        userManager = AuthUserManager()

        # Create the actual user data
        password = settings.DEFAULT_USER_PW
        if account_info.password:
            password = account_info.password
        user = User.objects.create_user(account_info.email, password)
        user.first_name = account_info.first_name
        user.last_name = account_info.last_name
        user.save()

        # Create the company_user data
        company_user = CompanyUser(
            company_id=account_info.company_id,
            user=user,
            company_user_type=account_info.company_user_type)

        if account_info.new_employee is not None:
            company_user.new_employee = account_info.new_employee

        company_user.save()

        # Now create the person object
        person_data = {
            'first_name': account_info.first_name,
            'last_name': account_info.last_name,
            'user': user.id,
            'relationship': SELF,
            'person_type': 'primary_contact',
            'email': user.email
        }

        person_serializer = PersonSimpleSerializer(data=person_data)
        if person_serializer.is_valid():
            person_serializer.save()
        else:
            raise Exception("Failed to create person record")

        # Create the employee profile
        key_service = HashKeyService()
        person_id = key_service.decode_key(person_serializer.data['id'])
        pin = account_info.pin
        pinService = EmployeePinService()
        if not pin:
            pin = pinService.get_company_wide_unique_pin(
                account_info.company_id, user.id)

        profile_data = {
            'person': person_id,
            'company': account_info.company_id,
            'start_date': account_info.start_date,
            'benefit_start_date': account_info.benefit_start_date,
            'pin': pin
        }

        if (account_info.compensation_info.annual_base_salary is not None):
            profile_data[
                'annual_base_salary'] = account_info.compensation_info.annual_base_salary

        if (account_info.employment_type):
            profile_data['employment_type'] = account_info.employment_type

        if (account_info.manager_id):
            profile_data['manager'] = account_info.manager_id

        if (account_info.employee_number):
            profile_data['employee_number'] = account_info.employee_number

        profile_serializer = EmployeeProfilePostSerializer(data=profile_data)

        if profile_serializer.is_valid():
            profile_serializer.save()
        else:
            raise Exception("Failed to create employee profile record")

        # Now check to see send email and create documents
        if company_user.company_user_type == 'employee':
            if account_info.send_email:
                # now try to create the onboard email for this user.
                try:
                    onboard_email(
                        "%s %s" % (user.first_name, user.last_name),
                        account_info.company_id,
                        [account_info.email, settings.SUPPORT_EMAIL_ADDRESS],
                        user.id)
                except StandardError:
                    raise Exception("Failed to send email to employee")

            if (account_info.create_docs):

                # Let's create the documents for this new user
                try:
                    doc_gen = UserDocumentGenerator(company_user.company, user)
                    doc_gen.generate_all_document(account_info.doc_fields)
                except Exception:
                    raise Exception(
                        "Failed to generate documents for employee")

            # Create the initial compensation record
            compensation_data = {
                'person': person_id,
                'company': account_info.company_id,
                'annual_base_salary':
                account_info.compensation_info.annual_base_salary,
                'projected_hour_per_month':
                account_info.compensation_info.projected_hour_per_month,
                'hourly_rate': account_info.compensation_info.hourly_rate,
                'effective_date':
                account_info.compensation_info.effective_date,
                'increase_percentage': None
            }

            compensation_serializer = EmployeeCompensationPostSerializer(
                data=compensation_data)

            if (compensation_serializer.is_valid()):
                compensation_serializer.save()
            else:
                raise Exception("Failed to create compensation record")

            if account_info.group_id:
                self._add_to_group(account_info.group_id, user.id)

            elif account_info.group_name:
                group_id = self._get_or_create_group(account_info.group_name,
                                                     account_info.company_id)
                if group_id:
                    self._add_to_group(group_id, user.id)
                else:
                    raise Exception(
                        "Cannot get group_id from group name {}".format(
                            account_info.group_name))

            account_info.user_id = user.id

            account_result.set_output_data(account_info)

            # Now for the new employee being created, setup any information
            # required for external parties (such as payroll and benefit service)
            # providers.
            self.company_integration_provider_data_service.generate_and_record_external_employee_number(
                user.id)

        return account_result
コード例 #17
0
class TestHashKeyService(TestCase):

    hash_key_service = HashKeyService()

    ''' encode a 'None' key, should return None
    '''
    def test_Encode_NoneKey_None(self):
        key = None
        encodedKey = self.hash_key_service.encode_key(key)
        self.assertTrue(not encodedKey)

	''' encode a valid key is expected to yield result with valid token format
    '''
    def test_Encode_Key_ValidFormat(self):
        key = 20
        encodedKey = self.hash_key_service.encode_key(key)
        regex = re.compile(HashKeyService.HASH_TOKEN_REGEX_PATTERN)
        self.assertTrue(not regex.match(encodedKey) is None)

    def test_Encode_Key_With_Environment_ValidFormat(self):
        key = 20
        encoded_env_key = self.hash_key_service.encode_key_with_environment(key)

        env_token = encoded_env_key.split('_', 1)[0]
        self.assertEqual(env_token, settings.ENVIRONMENT_IDENTIFIER)

        encoded_key = encoded_env_key.split('_', 1)[1]

        regex = re.compile(HashKeyService.HASH_TOKEN_REGEX_PATTERN)
        self.assertTrue(not regex.match(encoded_key) is None)

	''' decode a 'None' key, should return None
    '''
    def test_Decode_NoneKey_None(self):
        encodedkey = None
        key = self.hash_key_service.decode_key(encodedkey)
        self.assertTrue(not key)

	''' decode an invalid key, should return None
    '''
    def test_Decode_InvalidKey_None(self):
        encodedkey = 'This_Is_A_Bad_Token'
        key = self.hash_key_service.decode_key(encodedkey)
        self.assertTrue(not key)

	''' encode a valid key then decode it, should return original key
    '''
    def test_EncodeAndDecode_Key_OriginalKey(self):
        key = 20
        encodedKey = self.hash_key_service.encode_key(key)
        decodedKey = self.hash_key_service.decode_key(encodedKey)
        self.assertEqual(str(key), str(decodedKey))

    def test_Decode_With_Environment_InvalidEnvironment_None(self):
        key = 20
        encoded_key = self.hash_key_service.encode_key(key)
        encoded_env_key = "{0}_{1}".format('invalidEnv', encoded_key)
        decoded_key = self.hash_key_service.decode_key_with_environment(encoded_env_key)
        self.assertTrue(not decoded_key)

    def test_Decode_With_Environment_Valid_OriginalKey(self):
        key = 20
        encoded_env_key = self.hash_key_service.encode_key_with_environment(key)
        decoded_key = self.hash_key_service.decode_key_with_environment(encoded_env_key)
        self.assertTrue(str(key), str(decoded_key))
コード例 #18
0
 def _decode_value(self, value):
     hash_key_service = HashKeyService()
     return hash_key_service.decode_key(value)
コード例 #19
0
class TimeOffRecord(object):
    hash_key_service = HashKeyService()
    date_time_service = DateTimeService()

    def __init__(self, time_off_domain_model):
        if (time_off_domain_model is None):
            raise ValueError('Must pass valid time off domain model.')

        # List out instance variables
        self.requestor_user_id = None
        self.requestor_user_info = None
        self.approver_user_id = None
        self.approver_user_info = None
        self.start_date_time = None
        self.duration = None
        self.status = None
        self.decision_timestamp = None
        self.record_type = None
        self.request_timestamp = None

        # Parse requestor info
        requestor_user_descriptor = time_off_domain_model['requestor'][
            'personDescriptor']
        self.requestor_user_id = int(
            self.hash_key_service.decode_key_with_environment(
                requestor_user_descriptor))
        if (self.requestor_user_id):
            user_model = User.objects.get(pk=self.requestor_user_id)
            self.requestor_user_info = UserInfo(user_model)

        # Parse approver info
        if ('approver' in time_off_domain_model
                and time_off_domain_model['approver']):
            approver_user_descriptor = time_off_domain_model['approver'][
                'personDescriptor']
            self.approver_user_id = int(
                self.hash_key_service.decode_key_with_environment(
                    approver_user_descriptor))
            if (self.approver_user_id):
                user_model = User.objects.get(pk=self.approver_user_id)
                self.approver_user_info = UserInfo(user_model)

        # Parse record type
        self.record_type = time_off_domain_model['type']

        # Parse Duration
        self.duration = float(time_off_domain_model['duration'])

        # Parse status
        self.status = time_off_domain_model['status']

        # Parse all dates and times to objects
        self.start_date_time = self.date_time_service.parse_date_time(
            time_off_domain_model['startDateTime'])
        self.request_timestamp = self.date_time_service.parse_date_time(
            time_off_domain_model['requestTimestamp'])

        if ('decisionTimestamp' in time_off_domain_model):
            decision_time_str = time_off_domain_model['decisionTimestamp']
            self.decision_timestamp = self.date_time_service.parse_date_time(
                decision_time_str)

    @property
    def requestor_full_name(self):
        if (self.requestor_user_info is None):
            return None
        return self.requestor_user_info.full_name

    @property
    def approver_full_name(self):
        if (self.approver_user_info is None):
            return None
        return self.approver_user_info.full_name
コード例 #20
0
    def _encode_value(self, value):
        hash_key_service = HashKeyService()
        if not hash_key_service.is_encoded(value):
            return hash_key_service.encode_key(value)

        return value
コード例 #21
0
class UserByCredentialView(APIView):
    hash_key_service = HashKeyService()
    '''
    This is the view class to provide the API endpoint for getting user information
    by providing the username and password of the user.
    Note: The information got from this end point is simplified.
    '''
    authentication_classes = (CsrfExemptSessionAuthentication,
                              BasicAuthentication)

    def post(self, request, format=None):
        credential = request.DATA
        email = credential.get('email')
        password = credential.get('password')
        if not email or not password:
            return HttpResponse(status=401)
        auth_result = AuthenticationService().login(email, password, request)
        if auth_result.user:
            # Authentication successful.
            # Now get the user information
            result = self._get_user_data(auth_result.user)
            return Response(result)
        else:
            return HttpResponse(status=401)

    def _get_user_data(self, user):
        result = {}

        # User info
        user_info = {}

        ## Basic info
        user_info['user_id'] = user.id
        user_info[
            'user_id_env_encode'] = self.hash_key_service.encode_key_with_environment(
                user.id)
        user_info['account_email'] = user.email

        ## Person and Compensation Info
        persons = Person.objects.filter(user=user.id, relationship='self')
        if (len(persons) > 0):
            person_model = persons[0]
            person_data = PersonInfo(person_model)
            user_info['first_name'] = person_data.first_name
            user_info['last_name'] = person_data.last_name

            compensation_info = CompensationService(person_model=person_model)
            hourly_rate = compensation_info.get_current_hourly_rate()
            if (hourly_rate):
                hourly_rate = round(hourly_rate, 2)
            user_info['hourly_rate'] = hourly_rate

        result['user_info'] = user_info

        # Company Info
        company_info = {}

        company_users = CompanyUser.objects.filter(user=user.id)
        company_id = None
        if (len(company_users) > 0):
            company_user = company_users[0]
            company_id = company_user.company_id
            company_info['company_id'] = company_id
            company_info[
                'company_id_env_encode'] = self.hash_key_service.encode_key_with_environment(
                    company_user.company_id)
            if (company_user.company):
                company_info['company_name'] = company_user.company.name

        result['company_info'] = company_info

        # Application Features
        application_features = None
        if (company_id):
            application_feature_service = ApplicationFeatureService()
            application_features = application_feature_service.get_complete_application_feature_status_by_company(
                company_id)
            result['app_features_info'] = application_features

        # Projects
        if (application_features
                and application_features[APP_FEATURE_PROJECTMANAGEMENT]):
            project_service = ProjectService()
            result['project_list'] = project_service.get_projects_by_company(
                company_id, active_only=True)

        return result