Skip to content

Commit

Permalink
Merge pull request #36 from 3dem/rc0.0.4
Browse files Browse the repository at this point in the history
Release 0.0.4
  • Loading branch information
delarosatrevin authored Dec 8, 2020
2 parents 48be08a + e6407c3 commit cb4a84f
Show file tree
Hide file tree
Showing 11 changed files with 931 additions and 333 deletions.
4 changes: 3 additions & 1 deletion emhub/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
from glob import glob


__version__ = '0.0.3'
__version__ = '0.0.4'


def create_app(test_config=None):
Expand Down Expand Up @@ -115,6 +115,8 @@ def main():
kwargs['is_devel'] = app.is_devel
kwargs['version'] = __version__
kwargs['emhub_title'] = app.config.get('EMHUB_TITLE', '')
kwargs['possible_owners'] = app.dc.get_pi_labs()
kwargs.update(app.dc.get_resources_list())

return flask.render_template('main.html', **kwargs)

Expand Down
98 changes: 62 additions & 36 deletions emhub/data/data_content.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,11 @@ def _dateStr(self, datetime):
def get(self, **kwargs):
content_id = kwargs['content_id']
get_func_name = 'get_%s' % content_id.replace('-', '_') # FIXME
dataDict = {} # self.get_resources_list()
get_func = getattr(self, get_func_name, None)
return {} if get_func is None else get_func(**kwargs)
if get_func is not None:
dataDict.update(get_func(**kwargs))
return dataDict

def get_dashboard(self, **kwargs):
dataDict = self.get_resources_list()
Expand Down Expand Up @@ -219,41 +222,7 @@ def get_booking_calendar(self, **kwargs):
for a in dm.get_applications()
if a.is_active]

# Send a list of possible owners of bookings
# 1) Managers or admins can change the ownership to any user
# 2) Application managers can change the ownership to any user in their
# application
# 3) Other users can not change the ownership
user = self.app.user # shortcut
if user.is_manager:
piList = [u for u in dm.get_users() if u.is_pi]
elif user.is_application_manager:
apps = [a for a in user.created_applications if a.is_active]
piSet = {user.get_id()}
piList = [user]
for a in apps:
for pi in a.users:
if pi.get_id() not in piSet:
piList.append(pi)
elif user.is_pi:
piList = [user]
else:
piList = []

def _userjson(u):
return {'id': u.id, 'name': u.name}

# Group users by PI
labs = []
for u in piList:
if u.is_pi:
lab = [_userjson(u)] + [_userjson(u2) for u2 in u.get_lab_members()]
labs.append(lab)

if user.is_manager:
labs.append([_userjson(u) for u in self._get_facility_staff()])

dataDict['possible_owners'] = labs
dataDict['possible_owners'] = self.get_pi_labs()
dataDict['resource_id'] = kwargs.get('resource_id', None)
return dataDict

Expand Down Expand Up @@ -405,6 +374,27 @@ def get_portal_import_application(self, **kwargs):

return result

def get_reports_time_distribution(self, **kwargs):
#d = request.json or request.form
d = {'start': '2020-01-01',
'end': '2020-12-31'
}
bookings = self.app.dm.get_bookings_range(
datetime_from_isoformat(d['start']),
datetime_from_isoformat(d['end'])
)
func = self.app.dc.booking_to_event
bookings = [func(b) for b in bookings
if b.resource.is_microscope]

from emhub.reports import get_booking_counters
counters, cem_counters = get_booking_counters(bookings)

return {'overall': counters,
'cem': cem_counters,
'possible_owners': self.get_pi_labs()
}

# --------------------- Internal helper methods ---------------------------
def booking_to_event(self, booking):
""" Return a dict that can be used as calendar Event object. """
Expand Down Expand Up @@ -636,3 +626,39 @@ def _import_order_from_portal(self, orderCode):

return app

def get_pi_labs(self):
# Send a list of possible owners of bookings
# 1) Managers or admins can change the ownership to any user
# 2) Application managers can change the ownership to any user in their
# application
# 3) Other users can not change the ownership
user = self.app.user # shortcut
if user.is_manager:
piList = [u for u in self.app.dm.get_users() if u.is_pi]
elif user.is_application_manager:
apps = [a for a in user.created_applications if a.is_active]
piSet = {user.get_id()}
piList = [user]
for a in apps:
for pi in a.users:
if pi.get_id() not in piSet:
piList.append(pi)
elif user.is_pi:
piList = [user]
else:
piList = []

def _userjson(u):
return {'id': u.id, 'name': u.name}

# Group users by PI
labs = []
for u in piList:
if u.is_pi:
lab = [_userjson(u)] + [_userjson(u2) for u2 in u.get_lab_members()]
labs.append(lab)

if user.is_manager:
labs.append([_userjson(u) for u in self._get_facility_staff()])

return labs
36 changes: 20 additions & 16 deletions emhub/data/data_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,27 +453,31 @@ def __validate_booking(self, booking, **kwargs):
check_min_booking = kwargs.get('check_min_booking', True)
check_max_booking = kwargs.get('check_max_booking', True)

user = self._user
if not user.is_manager and not r.is_active:
raise Exception("Selected resource is inactive now. ")

if booking.start >= booking.end:
raise Exception("The booking 'end' should be after the 'start'. ")

if not user.is_manager and booking.start.date() < self.now().date():
raise Exception("The booking 'start' can not be in the past. ")

if check_min_booking and r.min_booking > 0:
mm = dt.timedelta(minutes=int(r.min_booking * 60))
if booking.duration < mm:
raise Exception("The duration of the booking is less that "
"the minimum specified for the resource. ")
user = self._user

if booking.type == 'booking' and check_max_booking and r.max_booking > 0:
mm = dt.timedelta(minutes=int(r.max_booking * 60))
if booking.duration > mm:
raise Exception("The duration of the booking is greater that "
"the maximum allowed for the resource. ")
# The following validations do not apply for managers
if not user.is_manager:
if r.is_active:
raise Exception("Selected resource is inactive now. ")

if booking.start.date() < self.now().date():
raise Exception("The booking 'start' can not be in the past. ")

if check_min_booking and r.min_booking > 0:
mm = dt.timedelta(minutes=int(r.min_booking * 60))
if booking.duration < mm:
raise Exception("The duration of the booking is less that "
"the minimum specified for the resource. ")

if booking.type == 'booking' and check_max_booking and r.max_booking > 0:
mm = dt.timedelta(minutes=int(r.max_booking * 60))
if booking.duration > mm:
raise Exception("The duration of the booking is greater that "
"the maximum allowed for the resource. ")

overlap = self.get_bookings_range(booking.start,
booking.end,
Expand Down
2 changes: 2 additions & 0 deletions emhub/reports/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

from .time_distribution import get_booking_counters
140 changes: 140 additions & 0 deletions emhub/reports/time_distribution.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@

import re


class Counter:
HEADERS = ["", "Bookings", "Days", "%"]
FORMAT = u"{:>15}{:>10}{:>10}{:>10}"
TOTAL = None

def __init__(self, name, filter=None):
self._name = name
self.counter = 0
self.days = 0
self._filter = filter or self._filter_by_name

def _filter_by_name(self, b):
return self._name.lower() in b['title'].lower()

def count(self, b):
if self._filter(b):
self.counter += 1
self.days += b['days']
return True
return False


class CounterList:
def __init__(self, *names):
self._counters = [
Counter('Total', lambda b: True),
Counter('Reminder', lambda b: True)
]
self.reminder = []

for n in names:
self.addCounter(n)

def addCounter(self, counter):
c = counter if isinstance(counter, Counter) else Counter(str(counter))
self._counters.insert(-1, c)

def count(self, b):
self._counters[0].count(b) # Always count total

def _any():
for c in self._counters[1:-1]:
if c.count(b):
return True
return False

if not _any():
self.reminder.append(b)
self._counters[-1].count(b) # Count reminder

def data(self):
total = self._counters[0].days
data = [[c._name, c.counter, c.days,
'%0.2f' % (c.days * 100 / total)]
for c in self._counters]

return data

def print(self):
format = Counter.FORMAT.format
print(format(*Counter.HEADERS))
for row in self.data():
print(format(*row))

def printReminder(self):
for b in self.reminder:
print(b['title'])


def is_maintainance(b):
t = b['title'].lower()
return any(k in t for k in ['cycle', 'installation', 'maintenance', 'afis'])

def is_developmnt(b):
t = b['title'].lower()
return any(k in t for k in ['method', 'research', 'tests', 'mikroed', 'microed'])


def get_cem(b):
title = b['title'].upper()

# Take only the first part of the application label
# (because it can contains the alias in parenthesis)
m = re.search("(CEM([0-9])+)", title)

if m is not None:
parsedCem = m.group(1).upper().strip()
# Enforce numeric part is exactly 5 digits
cemNumber = m.group(2)
n = len(cemNumber)
if n < 5:
cemNumber = "0" * (5 - n) + cemNumber
else:
cemNumber = cemNumber[-5:]

return 'CEM' + cemNumber

return None


class CemCounter(Counter):
def _filter_by_name(self, b):
return self._name.upper() == get_cem(b)


def get_booking_counters(bookings):
maintainance = Counter('Maintenance', is_maintainance)
development = Counter('Developmnt', is_developmnt)
CEM = Counter('CEM', filter=lambda b: get_cem(b) is not None)

counters = CounterList('Downtime', maintainance, 'DBB', CEM, development)
cem_counters = CounterList()

cem_dict = {}

for b in bookings:
title = b['title']

if 'Ume' in title:
continue

counters.count(b)

cem = get_cem(b)

if cem is not None:
c = cem_dict.get(cem, 0)
cem_dict[cem] = c + 1
if not c:
cem_counters.addCounter(CemCounter(cem))
cem_counters.count(b)

return counters, cem_counters



Loading

0 comments on commit cb4a84f

Please sign in to comment.