Source code for polo

import logging
import os
import re
from pathlib import Path
import sys
import inspect
import platform


from PyQt5.QtGui import QBrush, QColor, QIcon, QPixmap
from PyQt5 import QtWidgets
from tensorflow.contrib.predictor import from_saved_model

polo_version = '0.1.0'  # should be int.int.int format
dirname = Path(os.path.dirname(__file__)).parent


# CONSTANT FILE PATHS
# =============================================================================

LOG_PATH = Path('polo.log')  # always in same dir as Polo main file
RECENT_FILES = Path('recents.txt')
DATA_DIR = dirname.joinpath('data')
APP_ICON = DATA_DIR.joinpath('images/logos/polo.png')
UNRAR = dirname.joinpath('unrar')
TEMP_DIR = dirname.joinpath('.tmp')

BACKUP_DIR = Path(os.getcwd()).joinpath('.polo_backups')

if not TEMP_DIR.is_dir():
    os.makedirs(str(TEMP_DIR))

if not BACKUP_DIR.is_dir():
    os.makedirs(str(BACKUP_DIR))

if not RECENT_FILES.is_file():
    f = open(str(RECENT_FILES), 'w')
    f.close()
    

COCKTAIL_DATA_PATH = DATA_DIR.joinpath('cocktail_data')
COCKTAIL_META_DATA = COCKTAIL_DATA_PATH.joinpath('cocktail_meta.csv')
# cocktail meta data csv stores info about each of the cocktail menus. Stuff
# like when each menu was used and what type of screen it is (Soluble or 
# membrane screens)

# images to show when missing images or no image found 
DEFAULT_IMAGE_PATH, BLANK_IMAGE = (
    DATA_DIR.joinpath('images/default_images/default_image.jpg'),
    DATA_DIR.joinpath('images/default_images/blank_image.png')
)

# path to tensorflow marco model
MODEL_PATH = DATA_DIR.joinpath('savedmodel')


# HTML jinja2 templates
RUN_HTML_TEMPLATE = dirname.joinpath('templates/exportRunTemplate.html')
SCREEN_HTML_TEMPLATE = dirname.joinpath('templates/exportPlatesTemplate.html')
BLANK_IMAGE = Path('data/images/default_images/blank_image.png')

# icons for tabs and buttons of the GUI

ICONS = DATA_DIR.joinpath('images/icons')
ICON_DICT = {Path(icon).stem: ICONS.joinpath(icon)
             for icon in os.listdir(str(ICONS))}


# DATA
# =============================================================================

MODEL = from_saved_model(str(MODEL_PATH))  # load tensorflow model
ALLOWED_IMAGE_TYPES = {'.jpeg', '.png', '.jpg'}

IMAGE_CLASSIFICATIONS = [
    'Crystals', 'Clear', 'Precipitate', 'Other'
]

COLORS = {
    'red': QColor(199, 40, 58), 'blue': QColor(40, 133, 199),
    'green': QColor(95, 199, 40), 'orange': QColor(199, 127, 40),
    'yellow': QColor(199, 196, 40), 'grey': QColor(145, 145, 138),
    'purple': QColor(195, 24, 204), 'none': None
}

ALLOWED_IMAGE_COUNTS = [24, 96, 192, 384, 786, 1536]

IMAGE_SPECS = ['Visible', 'UV-TPEF', 'SHG', 'Other']
SPEC_KEYS = dict(zip(['jpg', 'uvt', 'shg', ''], IMAGE_SPECS))

MSO_DICT = {  # translate between marco encodings and mso encodings
    IMAGE_CLASSIFICATIONS[0]: 90,  # xtal
    #IMAGE_CLASSIFICATIONS[3]: 60,  # skin
    IMAGE_CLASSIFICATIONS[2]: 50,  # precipitate
    #IMAGE_CLASSIFICATIONS[2]: 25,  # phase
    IMAGE_CLASSIFICATIONS[1]: 10,  # clear
    #IMAGE_CLASSIFICATIONS[3]: 5,   # garbage
    IMAGE_CLASSIFICATIONS[3]: 0    # unsure
}
REV_MSO_DICT = {
    90: IMAGE_CLASSIFICATIONS[0],  # xtal
    60: IMAGE_CLASSIFICATIONS[3], # skin
    50: IMAGE_CLASSIFICATIONS[2],  # precipitate
    25: IMAGE_CLASSIFICATIONS[2],  # phase
    10: IMAGE_CLASSIFICATIONS[1],  # clear
    5: IMAGE_CLASSIFICATIONS[3],   # garbage
    0: IMAGE_CLASSIFICATIONS[3]   # unsure
}

# UNRAR EXE
# =============================================================================

unrar_versions = set([OS for OS in os.listdir(str(UNRAR))])
platform = platform.system()
if platform in unrar_versions:
    UNRAR_DIR = Path(os.path.join(str(UNRAR), platform))
else:
    UNRAR_DIR = False

if UNRAR_DIR:
    if platform == 'Windows':
        # get bits
        if sys.maxsize > 2**32:  # is 64 bit version
            UNRAR_DIR = UNRAR_DIR.joinpath('Win64')
        else:
            UNRAR_DIR = UNRAR_DIR.joinpath('Win32')
    UNRAR_EXE = [UNRAR_DIR.joinpath(f) for f in os.listdir(
        str(UNRAR_DIR)) if 'unrar' in f].pop()
else:
    UNRAR_EXE = Path('unrar')  # pray they have it installed and in their PATH
    
# REGEX
# =============================================================================
num_regex = re.compile('[-+]?([0-9]*\.[0-9]+|[0-9]+)')
peg_regex = re.compile('[0-9]+')
unit_regex = re.compile('v/v|w/v|M|L|ml|ul', re.I)
water_regex = re.compile('\*[0-9]*h2o', re.I)


# FUNCTIONS
# =============================================================================

[docs]def make_default_logger(name): logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) formatter = logging.Formatter( '%(levelname)s\t%(asctime)s\t%(name)s\t%(lineno)d\t%(message)s') file_handler = logging.FileHandler(str(LOG_PATH)) file_handler.setFormatter(formatter) logger.addHandler(file_handler) return logger
# URLS # ============================================================================= # link to the documentation site HOST_PREFIX = 'https://hauptman-woodward.github.io/' ABOUT = HOST_PREFIX + 'Marco_Polo/about.html' QUICKSTART = HOST_PREFIX + 'Marco_Polo/Quickstart.html' FAQS = HOST_PREFIX + 'Marco_Polo/FAQS.html' USER_GUIDE = HOST_PREFIX + 'Marco_Polo/user_guide.html' DOCS = HOST_PREFIX + 'Marco_Polo/polo.html' BETA = HOST_PREFIX + 'Marco_Polo/beta_testers.html' REPORTS = HOST_PREFIX + 'Marco_Polo/reports.html' RELEASES = 'https://github.com/Hauptman-Woodward/Marco_Polo/tags' MARCO_ARTICLE = 'https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0198883' POLO_ARTICLE = 'https://journals.iucr.org/j/issues/2021/02/00/ei5066/index.html' POLO_CITATION = ''' Holleman, E.T., Duguid, E., Keefe, L.J. & Bowman, S.E.J. (2021). J. Appl. Cryst. 54, https://doi.org/10.1107/S1600576721000108 ''' MARCO_CITATION = ''' Bruno AE, Charbonneau P, Newman J, Snell EH, So DR, et al. (2018) Classification of crystallization outcomes using deep convolutional neural networks. PLOS ONE 13(6): e0198883. https://doi.org/10.1371/journal.pone.0198883 ''' # RUN_TYPES = sorted( # [types[-1] for types in # inspect.getmembers(sys.modules['polo.crystallography.run'], inspect.isclass) # if issubclass(types[-1], Run)], # key=lambda c: c.import_priority, # reverse=True) # get all classes in the Run module that are subclassed from Run this is # used for imports. Sort th # logger = make_default_logger(__name__) # logger.debug('Detected OS: {}'.format(platform)) # logger.debug('Working directory: {}'.format(os.getcwd())) # logger.debug('Polo directory: {}'.format(dirname)) critical_paths = [ MODEL_PATH, COCKTAIL_DATA_PATH, COCKTAIL_META_DATA, BACKUP_DIR, TEMP_DIR, DATA_DIR ] # for path in critical_paths: # if path.exists(): # logger.debug('Critical path {} checked'.format(path)) # else: # logger.critical('Critical path {} does not exist!'.format(path)) from polo.utils.io_utils import BarTender, Menu bartender = BarTender(str(COCKTAIL_DATA_PATH), str(COCKTAIL_META_DATA)) from polo.crystallography.image import * from polo.crystallography.cocktail import * from polo.crystallography.run import * from polo.threads import *