Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,26 @@ env ?= dev # get from cl or 'dev' by default
# make build
# make build env=prod
build:
docker-compose build \
docker compose build \
--build-arg BUILD_ENV=$(env) \
$(CONTAINER_SERVER_NAME) && \
docker-compose build \
docker compose build \
--build-arg BUILD_ENV=$(env) \
$(CONTAINER_CLIENT_NAME)

build-force:
docker-compose build --force \
build-nocache:
docker compose build --no-cache \
--build-arg BUILD_ENV=$(env) \
$(CONTAINER_SERVER_NAME) && \
docker-compose build --force \
docker compose build --no-cache \
--build-arg BUILD_ENV=$(env) \
$(CONTAINER_CLIENT_NAME)

up:
docker-compose up -d $(CONTAINER_SERVER_NAME)
docker compose up -d $(CONTAINER_SERVER_NAME)

down:
docker-compose down
docker compose down

google-auth:
python3 scripts/google_auth.py
Expand Down
17 changes: 17 additions & 0 deletions config.tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
eval_process:
id: '2025 Q3'
due_date: '12:00 PM, noon, July 25, 2025'
feature_disabling:
send_eval_reports_as_pdf: False
generate_google_doc_from_eval_report: False
add_comenter_to_eval_reports: False

providers:
storage: "mock"
communication_channel: "mock"
forms_platform: "mock"

company:
domain: "company.com"
number_of_employees: 1000
9 changes: 9 additions & 0 deletions evalytics/communications_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,17 @@ def get_communication_channel(self):
elif communication_channel_provider == ProvidersConfig.SLACK:
return SlackChannel()

elif communication_channel_provider == ProvidersConfig.MOCK:
return MockChannel()

raise ValueError(communication_channel_provider)


class MockChannel:

def send_communication(self, reviewer, kind: CommunicationKind):
return

class SlackClient:

__client = None
Expand Down
7 changes: 6 additions & 1 deletion evalytics/config.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import yaml
from tornado.options import options

class ConfigReader:

CONFIG_FILE = 'config.yaml'

# TODO: define strategy to read real-time config values without using this function so much
# - some caching strategy perhaps?
def read(self):
with open(self.CONFIG_FILE, 'r') as stream:
with open(options.config, 'r') as stream:
data_loaded = yaml.safe_load(stream)

return data_loaded
Expand All @@ -18,6 +21,8 @@ class ProvidersConfig(ConfigReader):
COMMUNICATION_CHANNEL = 'communication_channel'
FORMS_PLATFORM = 'forms_platform'

MOCK = "mock"

GOOGLE_DRIVE = "google_drive"

GMAIL = "gmail"
Expand Down
18 changes: 18 additions & 0 deletions evalytics/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ def get_key(self, data_kind, reviewer_response: ReviewerResponse):
raise NotImplementedError(
'ReviewerResponseKeyDictStrategy does not implement {} strategy'.format(data_kind))


class MockForms:
'''
Harcoded forms for testing purposes
'''

def get_peers_assignment(self):
pass

def get_responses(self):
response_kind = ReviewerResponseKeyDictStrategy.REVIEWER_RESPONSE
pass

def get_evaluations(self):
response_kind = ReviewerResponseKeyDictStrategy.REVIEWEE_EVALUATION
pass


class GoogleForms(GoogleAPI, ResponseFileNameToEvalKind, Config):

def get_peers_assignment(self):
Expand Down
44 changes: 31 additions & 13 deletions evalytics/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@
from evalytics.exceptions import MissingDataException, NoFormsException


VERSION = "0.1.0"
class HealthHandler(tornado.web.RequestHandler):
path = r"/health"

async def get(self):
self.set_status(200)
self.set_header("API-Version", VERSION)
self.finish()
return


# TODO: control response when no config.yaml
# TODO: control response when no credentials.json
class EmployeesHandler(tornado.web.RequestHandler):
path = r"/employees"

Expand All @@ -30,21 +43,25 @@ async def get(self):
'employees': [Mapper().employee_to_json(e) for uid, e in employees.items()]
}
})
return
except (MissingGoogleDriveFolderException,
MissingGoogleDriveFileException) as e:
self.finish({
'success': False,
'response': {
'error': e.message,
}
})
except (MissingDataException, NoFormsException) as exception:
self.finish({
'success': False,
'response': {
'error': exception.message,
}
})
self.set_status(500)
message = e.message
except (MissingDataException, NoFormsException) as e:
self.set_status(500)
message = e.message
except FileNotFoundError as e:
self.set_status(500)
message = f'FileNotFoundError: {str(e)}'

self.finish({
'success': False,
'response': {
'error': message,
}
})



class SurveysHandler(tornado.web.RequestHandler):
Expand Down Expand Up @@ -186,6 +203,7 @@ async def get(self):
}
})

# TODO: review if this has to be exposed in an API
class CommunicationHandler(tornado.web.RequestHandler):
path = r"/communications"

Expand Down
73 changes: 73 additions & 0 deletions evalytics/storages.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,94 @@
from googledrive.exceptions import MissingGoogleDriveFileException
from googledrive.api import GoogleDrive

import csv

from evalytics.google_api import GoogleAPI
from evalytics.config import Config, ProvidersConfig
from evalytics.models import Employee, EvalKind
from evalytics.models import ReviewerResponse
from evalytics.exceptions import MissingDataException, NoFormsException


class StorageFactory(Config):

def get_storage(self):
storage_kind = super().read_storage_provider()
if storage_kind == ProvidersConfig.GOOGLE_DRIVE:
return GoogleStorage()

elif storage_kind == ProvidersConfig.MOCK:
return MockStorage()

raise ValueError(storage_kind)

class MockStorage(Config):
'''
Harcoded storage for testing purposes
'''

def get_employees(self):
employees = {}
with open('mock/storage/0-org-chart.csv', mode ='r') as file:
file.readline() # discarding headers
csvFile = csv.reader(file)
for line in csvFile:
employee = Employee(
mail=line[0],
manager=line[1],
area=line[2])
employees.update({employee.uid : employee})

return employees

def get_forms(self):
forms = {}
with open('mock/storage/0-form-map.csv', mode ='r') as file:
file.readline() # discarding headers
csvFile = csv.reader(file)
for line in csvFile:
form_area = line[0].strip()
self_eval = line[1]
peer_manager_eval = line[2]
manager_peer_eval = line[3]
peer_to_peer_eval = line[4]

forms.update({
form_area: {
EvalKind.SELF.name: self_eval,
EvalKind.PEER_MANAGER.name: peer_manager_eval,
EvalKind.MANAGER_PEER.name: manager_peer_eval,
EvalKind.PEER_TO_PEER.name: peer_to_peer_eval,
}
})

return forms

def generate_eval_reports(self,
reviewee,
reviewee_evaluations: ReviewerResponse,
employee_managers):
pass

def get_peers_assignment(self):
# Creating models
peers = {}
with open('mock/storage/1-peers-assignment.csv', mode ='r') as file:
file.readline() # discarding headers
csvFile = csv.reader(file)
for line in csvFile:
reviewer = line[0].strip()
reviewees = list(map(str.strip, line[1].split(';')))

peers.update({
reviewer: reviewees
})

return peers

def write_peers_assignment(self, peers_assignment):
return

class GoogleStorage(GoogleAPI, Config):

def get_employees(self):
Expand Down
3 changes: 3 additions & 0 deletions mock/storage/0-form-map.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Form Kind, self eval, peer-manager eval, manager-peer eval, peer-peer eval
Eng, self-eval.eng.localhost, peer2manager.eng.localhost, manager2peer.eng.localhost, peer2peer.eng.localhost
Product, self-eval.product.localhost, peer2manager.product.localhost, manager2peer.product.localhost, peer2peer.product.localhost
8 changes: 8 additions & 0 deletions mock/storage/0-org-chart.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Email ID, Manager ID, Team
fulanite@company.io, techlead, Eng
ctanganite@company.io, techlead, Eng
menganita@company.io, techlead, Eng
techlead@company.io, CTO, Eng
CIO@company.io, CEO, Eng
CTO@company.io, CEO, Eng
CEO@company.io, /dev/null, Eng
8 changes: 8 additions & 0 deletions mock/storage/1-peers-assignment.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
reviewer, reviewees
fulanite, ctanganite;menganita;techlead
ctanganite, fulanite;menganita;techlead
menganita, fulanite;ctanganite;techlead
techlead, fulanite;ctanganite;menganita;CTO
CIO, CTO;CEO;ctanganite
CTO, techlead;CEO
CEO, CTO;CIO;techlead
Loading