<commit_before>import numpy as np
import pandas as pd
from bokeh.plotting import *
from six.moves import cStringIO as StringIO
from math import log, sqrt
from collections import OrderedDict

antibiotics = """
bacteria,                        penicillin, streptomycin, neomycin, gram
Mycobacterium tuberculosis,      800,        5,            2,        negative
Salmonella schottmuelleri,       10,         0.8,          0.09,     negative
Proteus vulgaris,                3,          0.1,          0.1,      negative
Klebsiella pneumoniae,           850,        1.2,          1,        negative
Brucella abortus,                1,          2,            0.02,     negative
Pseudomonas aeruginosa,          850,        2,            0.4,      negative
Escherichia coli,                100,        0.4,          0.1,      negative
Salmonella (Eberthella) typhosa, 1,          0.4,          0.008,    negative
Aerobacter aerogenes,            870,        1,            1.6,      negative
Brucella antracis,               0.001,      0.01,         0.007,    positive
Streptococcus fecalis,           1,          1,            0.1,      positive
Staphylococcus aureus,           0.03,       0.03,         0.001,    positive
Staphylococcus albus,            0.007,      0.1,          0.001,    positive
Streptococcus hemolyticus,       0.001,      14,           10,       positive
Streptococcus viridans,          0.005,      10,           40,       positive
Diplococcus pneumoniae,          0.005,      11,           10,       positive
"""

drug_color = OrderedDict([
    ("Penicillin",   "#0d3362"),
    ("Streptomycin", "#c64737"),
    ("Neomycin",     "black"  ),
])

gram_color = {
    "positive" : "#aeaeb8",
    "negative" : "#e69584",
}

df = pd.read_csv(StringIO(antibiotics), skiprows=1, skipinitialspace=True)

width = 800
height = 800
inner_radius = 90
outer_radius = 300 - 10

minr = sqrt(log(.001 * 1E4))
maxr = sqrt(log(1000 * 1E4))
a = (outer_radius - inner_radius) / (minr - maxr)
b = inner_radius - a * maxr

def rad(mic):
    return a * np.sqrt(np.log(mic * 1E4)) + b

big_angle = 2.0 * np.pi / (len(df) + 1)
small_angle = big_angle / 7

x = np.zeros(len(df))
y = np.zeros(len(df))

p = figure(plot_width=width, plot_height=height, title="",
           tools="pan,wheel_zoom,box_zoom,reset,previewsave",
           x_axis_type=None, y_axis_type=None,
           x_range=[-420, 420], y_range=[-420, 420],
           min_border=0, outline_line_color=None,
           background_fill="#f0e1d2", border_fill="#f0e1d2")

p.line(x+1, y+1, alpha=0)

# annular wedges
angles = np.pi/2 - big_angle/2 - df.index.to_series()*big_angle
colors = [gram_color[gram] for gram in df.gram]
p.annular_wedge(
    x, y, inner_radius, outer_radius, -big_angle+angles, angles, color=colors,
)

# small wedges
p.annular_wedge(
    x, y, inner_radius, rad(df.penicillin), -big_angle+angles + 5*small_angle, -big_angle+angles+6*small_angle, color=drug_color['Penicillin'],
)
p.annular_wedge(
    x, y, inner_radius, rad(df.streptomycin), -big_angle+angles + 3*small_angle, -big_angle+angles+4*small_angle, color=drug_color['Streptomycin'],
)
p.annular_wedge(
    x, y, inner_radius, rad(df.neomycin), -big_angle+angles + 1*small_angle, -big_angle+angles+2*small_angle, color=drug_color['Neomycin'],
)

# circular axes and lables
labels = np.power(10.0, np.arange(-3, 4))
radii = a * np.sqrt(np.log(labels * 1E4)) + b
p.circle(x, y, radius=radii, fill_color=None, line_color="white")
p.text(x[:-1], radii[:-1], [str(r) for r in labels[:-1]], angle=0, text_font_size="8pt", text_align="center", text_baseline="middle")

# radial axes
p.annular_wedge(
    x, y, inner_radius-10, outer_radius+10, -big_angle+angles, -big_angle+angles, color="black",
)

# bacteria labels
xr = radii[0]*np.cos(np.array(-big_angle/2 + angles))
yr = radii[0]*np.sin(np.array(-big_angle/2 + angles))
label_angle=np.array(-big_angle/2+angles)
label_angle[label_angle < -np.pi/2] += np.pi # easier to read labels on the left side
p.text(xr, yr, df.bacteria, angle=label_angle,
    text_font_size="9pt", text_align="center", text_baseline="middle")

# OK, these hand drawn legends are pretty clunky, will be improved in future release
p.circle([-40, -40], [-370, -390], color=list(gram_color.values()), radius=5)
p.text([-30, -30], [-370, -390], text=["Gram-" + x for x in gram_color.keys()],
    angle=0, text_font_size="7pt", text_align="left", text_baseline="middle")

p.rect([-40, -40, -40], [18, 0, -18], width=30, height=13,
    color=list(drug_color.values()))
p.text([-15, -15, -15], [18, 0, -18], text=list(drug_color.keys()),
    angle=0, text_font_size="9pt", text_align="left", text_baseline="middle")

p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None

output_file("burtin.html", title="burtin.py example")
show(p)
<commit_msg>consistent formatting<commit_after>import numpy as np
import pandas as pd
from bokeh.plotting import *
from six.moves import cStringIO as StringIO
from math import log, sqrt
from collections import OrderedDict

antibiotics = """
bacteria,                        penicillin, streptomycin, neomycin, gram
Mycobacterium tuberculosis,      800,        5,            2,        negative
Salmonella schottmuelleri,       10,         0.8,          0.09,     negative
Proteus vulgaris,                3,          0.1,          0.1,      negative
Klebsiella pneumoniae,           850,        1.2,          1,        negative
Brucella abortus,                1,          2,            0.02,     negative
Pseudomonas aeruginosa,          850,        2,            0.4,      negative
Escherichia coli,                100,        0.4,          0.1,      negative
Salmonella (Eberthella) typhosa, 1,          0.4,          0.008,    negative
Aerobacter aerogenes,            870,        1,            1.6,      negative
Brucella antracis,               0.001,      0.01,         0.007,    positive
Streptococcus fecalis,           1,          1,            0.1,      positive
Staphylococcus aureus,           0.03,       0.03,         0.001,    positive
Staphylococcus albus,            0.007,      0.1,          0.001,    positive
Streptococcus hemolyticus,       0.001,      14,           10,       positive
Streptococcus viridans,          0.005,      10,           40,       positive
Diplococcus pneumoniae,          0.005,      11,           10,       positive
"""

drug_color = OrderedDict([
    ("Penicillin",   "#0d3362"),
    ("Streptomycin", "#c64737"),
    ("Neomycin",     "black"  ),
])

gram_color = {
    "positive" : "#aeaeb8",
    "negative" : "#e69584",
}

df = pd.read_csv(StringIO(antibiotics), skiprows=1, skipinitialspace=True)

width = 800
height = 800
inner_radius = 90
outer_radius = 300 - 10

minr = sqrt(log(.001 * 1E4))
maxr = sqrt(log(1000 * 1E4))
a = (outer_radius - inner_radius) / (minr - maxr)
b = inner_radius - a * maxr

def rad(mic):
    return a * np.sqrt(np.log(mic * 1E4)) + b

big_angle = 2.0 * np.pi / (len(df) + 1)
small_angle = big_angle / 7

x = np.zeros(len(df))
y = np.zeros(len(df))

p = figure(plot_width=width, plot_height=height, title="",
    x_axis_type=None, y_axis_type=None,
    x_range=[-420, 420], y_range=[-420, 420],
    min_border=0, outline_line_color=None,
    background_fill="#f0e1d2", border_fill="#f0e1d2")

p.line(x+1, y+1, alpha=0)

# annular wedges
angles = np.pi/2 - big_angle/2 - df.index.to_series()*big_angle
colors = [gram_color[gram] for gram in df.gram]
p.annular_wedge(
    x, y, inner_radius, outer_radius, -big_angle+angles, angles, color=colors,
)

# small wedges
p.annular_wedge(x, y, inner_radius, rad(df.penicillin),
    -big_angle+angles+5*small_angle, -big_angle+angles+6*small_angle,
    color=drug_color['Penicillin'])
p.annular_wedge(x, y, inner_radius, rad(df.streptomycin),
    -big_angle+angles+3*small_angle, -big_angle+angles+4*small_angle,
    color=drug_color['Streptomycin'])
p.annular_wedge(x, y, inner_radius, rad(df.neomycin),
    -big_angle+angles+1*small_angle, -big_angle+angles+2*small_angle,
    color=drug_color['Neomycin'])

# circular axes and lables
labels = np.power(10.0, np.arange(-3, 4))
radii = a * np.sqrt(np.log(labels * 1E4)) + b
p.circle(x, y, radius=radii, fill_color=None, line_color="white")
p.text(x[:-1], radii[:-1], [str(r) for r in labels[:-1]],
    angle=0, text_font_size="8pt",
    text_align="center", text_baseline="middle")

# radial axes
p.annular_wedge(x, y, inner_radius-10, outer_radius+10,
    -big_angle+angles, -big_angle+angles, color="black")

# bacteria labels
xr = radii[0]*np.cos(np.array(-big_angle/2 + angles))
yr = radii[0]*np.sin(np.array(-big_angle/2 + angles))
label_angle=np.array(-big_angle/2+angles)
label_angle[label_angle < -np.pi/2] += np.pi # easier to read labels on the left side
p.text(xr, yr, df.bacteria, angle=label_angle,
    text_font_size="9pt", text_align="center", text_baseline="middle")

# OK, these hand drawn legends are pretty clunky, will be improved in future release
p.circle([-40, -40], [-370, -390], color=list(gram_color.values()), radius=5)
p.text([-30, -30], [-370, -390], text=["Gram-" + x for x in gram_color.keys()],
    angle=0, text_font_size="7pt", text_align="left", text_baseline="middle")

p.rect([-40, -40, -40], [18, 0, -18], width=30, height=13,
    color=list(drug_color.values()))
p.text([-15, -15, -15], [18, 0, -18], text=list(drug_color.keys()),
    angle=0, text_font_size="9pt", text_align="left", text_baseline="middle")

p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None

output_file("burtin.html", title="burtin.py example")
show(p)
<|endoftext|>


<commit_before>import logging
import os
import sys
import tkinter
from tkinter import ttk
sys.path.append('../..')

import cv2
from src.image.imnp import ImageNP
from src.support.tkconvert import TkConverter
from src.view.template import TkViewer
from src.view.tkfonts import TkFonts
from src.view.tkframe import TkFrame, TkLabelFrame
from src.view.ttkstyle import TTKStyle, init_css

LOGGER = logging.getLogger(__name__)

THRESHOLD_OPTION = [(u'手動', 'manual'), ('Mean Adaptive', 'mean'), ('Gaussian Adaptive', 'gaussian')]

class GraphCutViewer(TkViewer):
    def __init__(self):
        super().__init__()
        self._im_w, self._im_h = 800, 533
        self._init_window(zoom=False)
        self._init_style()
        self._init_frame()

    def _init_style(self):
        init_css()
        TTKStyle('H4Padding.TLabelframe', background='gray82')
        TTKStyle('H4Padding.TLabelframe.Label',  font=('', 16), background='gray82')
        TTKStyle('H2BlackdBold.TLabel', font=('', 24, 'bold'), background='white', foreground='black')
        TTKStyle('H2RedBold.TLabel', font=('', 24, 'bold'), background='white', foreground='red')
        self.font = TkFonts()

    # init frame
    def _init_frame(self):
        # root
        self.frame_root = TkFrame(self.root, bg='white')
        self.frame_root.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_root, 0, 1, 2)
        self.set_all_grid_columnconfigure(self.frame_root, 0)

        # head
        self.frame_head = TkFrame(self.frame_root, bg='white')
        self.frame_head.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_head, 0)
        self.set_all_grid_columnconfigure(self.frame_head, 0)

        # body
        self.frame_body = TkFrame(self.frame_root, bg='black')
        self.frame_body.grid(row=1, column=0, sticky='news')
        self.set_all_grid_columnconfigure(self.frame_body, 0, 1)
        self.set_all_grid_rowconfigure(self.frame_body, 0)

        # body > panel
        self.frame_panel = TkFrame(self.frame_body, bg='light pink')
        self.frame_panel.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_panel, 0)
        self.set_all_grid_columnconfigure(self.frame_panel, 0)

        # body > display
        self.frame_display = TkFrame(self.frame_body, bg='royal blue')
        self.frame_display.grid(row=0, column=1, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_display, 0)
        self.set_all_grid_columnconfigure(self.frame_display, 0)

        # footer
        self.frame_footer = TkFrame(self.frame_root, bg='gray82')
        self.frame_footer.grid(row=2, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_footer, 0, 1)
        self.set_all_grid_columnconfigure(self.frame_footer, 0)

        # footer > panel setting
        self.frame_panel_setting = ttk.LabelFrame(self.frame_footer, text=u'輸入圖片選項: ', style='H4Padding.TLabelframe')
        self.frame_panel_setting.grid(row=0, column=0, sticky='news', pady=10)
        self.set_all_grid_rowconfigure(self.frame_panel_setting, 0, 1)
        self.set_all_grid_columnconfigure(self.frame_panel_setting, 0)

        # footer > panel setting > template option
        self.frame_template_options = TkFrame(self.frame_panel_setting, bg='gray82', pady=5)
        self.frame_template_options.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_template_options, 0)
        self.set_all_grid_columnconfigure(self.frame_template_options, 0)

        # footer > panel setting > gamma
        self.frame_gamma = TkFrame(self.frame_panel_setting, bg='gray82', pady=5)
        self.frame_gamma.grid(row=1, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_gamma, 0)
        self.set_all_grid_columnconfigure(self.frame_gamma, 0)

        # footer > display setting
        self.frame_display_setting = ttk.LabelFrame(self.frame_footer, text=u'輸出圖片選項: ', style='H4Padding.TLabelframe')
        self.frame_display_setting.grid(row=1, column=0, sticky='news', pady=10)
        self.set_all_grid_rowconfigure(self.frame_display_setting, 0)
        self.set_all_grid_columnconfigure(self.frame_display_setting, 0)

        # footer > display setting > threshold options
        self.frame_threshold_options = TkFrame(self.frame_display_setting, bg='gray82', pady=5)
        self.frame_threshold_options.grid(row=0, column=0, sticky='news')

        # footer > display setting > manual threshold
        self.frame_manual_threshold = TkFrame(self.frame_display_setting, bg='gray82', pady=5)
        self.frame_manual_threshold.grid(row=1, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_manual_threshold, 0)
        self.set_all_grid_columnconfigure(self.frame_manual_threshold, 0)

        self._init_widget_head()
        self._init_widget_body()
        self._init_widget_footer()

    # init head widget
    def _init_widget_head(self):
        self.set_all_grid_rowconfigure(self.frame_head, 0, 1)
        self.label_state = ttk.Label(self.frame_head, text=u'現在模式: N/A', style='H2.TLabel')
        self.label_state.grid(row=0, column=0, sticky='w')
        self.label_resize = ttk.Label(self.frame_head, text=u'原有尺寸 N/A-> 顯示尺寸 N/A', style='H2.TLabel')
        self.label_resize.grid(row=1, column=0, sticky='w')

    # init body widget
    def _init_widget_body(self):
        # panel
        self.set_all_grid_rowconfigure(self.frame_panel, 0, 1)
        self.label_panel = ttk.Label(self.frame_panel, text='Input Panel', style='H2.TLabel')
        self.label_panel.grid(row=0, column=0, sticky='ns')
        self.photo_panel = ImageNP.generate_checkboard((self._im_h, self._im_w), block_size=10)
        self.photo_panel = TkConverter.ndarray_to_photo(self.photo_panel)
        self.label_panel_image = ttk.Label(self.frame_panel, image=self.photo_panel)
        self.label_panel_image.grid(row=1, column=0, sticky='ns')

        # display
        self.label_display = ttk.Label(self.frame_display, text='Display', style='H2.TLabel')
        self.label_display.grid(row=0, column=0, sticky='ns', columnspan=3)

        self.set_all_grid_rowconfigure(self.frame_display, 0, 1, 2)
        self.set_all_grid_columnconfigure(self.frame_display, 0, 1, 2)
        self.photo_small = ImageNP.generate_checkboard((self._im_h//2, self._im_w//3), 10)
        self.photo_small = TkConverter.ndarray_to_photo(self.photo_small)
        self.photo_large = ImageNP.generate_checkboard((self._im_h, self._im_w//3), 10)
        self.photo_large = TkConverter.ndarray_to_photo(self.photo_large)
        self.label_fl_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_fl_image.grid(row=1, column=0, sticky='ns')
        self.label_fr_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_fr_image.grid(row=1, column=1, sticky='ns')
        self.label_bl_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_bl_image.grid(row=2, column=0, sticky='ns')
        self.label_br_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_br_image.grid(row=2, column=1, sticky='ns')
        self.label_body_image = ttk.Label(self.frame_display, image=self.photo_large)
        self.label_body_image.grid(row=1, column=2, sticky='ns', rowspan=2)

    # init footer widget
    def _init_widget_footer(self):
        # input panel template option
        self.label_template = ttk.Label(self.frame_template_options, text=u'過濾樣式: ', style='H5.TLabel')
        self.label_template.grid(row=0, column=0, sticky='w')

        # input panel gamma
        self.label_gamma = ttk.Label(self.frame_gamma, text=u'調整對比 ({:.2f}): '.format(1.), style='H5.TLabel')
        self.label_gamma.grid(row=0, column=0, sticky='w')
        self.val_scale_gamma = tkinter.DoubleVar()
        self.val_scale_gamma.set(1.0)
        self.scale_gamma = ttk.Scale(self.frame_gamma,
                                     orient=tkinter.HORIZONTAL,
                                     length=self._im_w*2,
                                     from_=0, to=25,
                                     variable=self.val_scale_gamma,
                                     style='Gray.Horizontal.TScale')
        self.scale_gamma.state(('active', '!disabled'))
        self.scale_gamma.grid(row=0, column=1, sticky='w')

        # display threshold option
        self.label_threshold_options = ttk.Label(self.frame_threshold_options, text=u'門檻值選項: ', style='H5.TLabel')
        self.label_threshold_options.grid(row=0, column=0, sticky='w')
        self.val_threshold_option = tkinter.StringVar()
        self.val_threshold_option.set(THRESHOLD_OPTION[0][-1])
        self.radiobtn_threshold_options = []
        for i, op in enumerate(THRESHOLD_OPTION):
            text, val = op
            radiobtn = ttk.Radiobutton(self.frame_threshold_options,
                                       text=text,
                                       variable=self.val_threshold_option,
                                       value=val,
                                       style='H5.TRadiobutton')
            radiobtn.grid(row=0, column=i+1, sticky='w', padx=10)
            self.radiobtn_threshold_options.append(radiobtn)

        # display threshold manual scale
        self.label_manual_threshold = ttk.Label(self.frame_manual_threshold, text=u'門檻值 ({:.2f}): '.format(250), style='H5.TLabel')
        self.label_manual_threshold.grid(row=0, column=0, sticky='w')
        self.val_manual_threshold = tkinter.DoubleVar()
        self.val_manual_threshold.set(250)
        self.scale_manual_threshold = ttk.Scale(self.frame_manual_threshold,
                                                orient=tkinter.HORIZONTAL,
                                                length=self._im_w*2,
                                                from_=1, to=254,
                                                variable=self.val_manual_threshold,
                                                style='Gray.Horizontal.TScale')
        self.scale_manual_threshold.state(('active', '!disabled'))
        self.scale_manual_threshold.grid(row=0, column=1, sticky='news', columnspan=len(THRESHOLD_OPTION))

if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(filename)12s:L%(lineno)3s [%(levelname)8s] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
        stream=sys.stdout
    )

    graphcut_viewer = GraphCutViewer()
    graphcut_viewer.mainloop()
<commit_msg>remove: sticky ns for each component<commit_after>import logging
import os
import sys
import tkinter
from tkinter import ttk
sys.path.append('../..')

import cv2
from src.image.imnp import ImageNP
from src.support.tkconvert import TkConverter
from src.view.template import TkViewer
from src.view.tkfonts import TkFonts
from src.view.tkframe import TkFrame, TkLabelFrame
from src.view.ttkstyle import TTKStyle, init_css

LOGGER = logging.getLogger(__name__)

THRESHOLD_OPTION = [(u'手動', 'manual'), ('Mean Adaptive', 'mean'), ('Gaussian Adaptive', 'gaussian')]

class GraphCutViewer(TkViewer):
    def __init__(self):
        super().__init__()
        self._im_w, self._im_h = 800, 533
        self._init_window(zoom=False)
        self._init_style()
        self._init_frame()

    def _init_style(self):
        init_css()
        TTKStyle('H4Padding.TLabelframe', background='gray82')
        TTKStyle('H4Padding.TLabelframe.Label',  font=('', 16), background='gray82')
        TTKStyle('H2BlackdBold.TLabel', font=('', 24, 'bold'), background='white', foreground='black')
        TTKStyle('H2RedBold.TLabel', font=('', 24, 'bold'), background='white', foreground='red')
        self.font = TkFonts()

    # init frame
    def _init_frame(self):
        # root
        self.frame_root = TkFrame(self.root, bg='white')
        self.frame_root.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_root, 0, 1, 2)
        self.set_all_grid_columnconfigure(self.frame_root, 0)

        # head
        self.frame_head = TkFrame(self.frame_root, bg='white')
        self.frame_head.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_head, 0)
        self.set_all_grid_columnconfigure(self.frame_head, 0)

        # body
        self.frame_body = TkFrame(self.frame_root, bg='black')
        self.frame_body.grid(row=1, column=0, sticky='news')
        self.set_all_grid_columnconfigure(self.frame_body, 0, 1)
        self.set_all_grid_rowconfigure(self.frame_body, 0)

        # body > panel
        self.frame_panel = TkFrame(self.frame_body, bg='light pink')
        self.frame_panel.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_panel, 0)
        self.set_all_grid_columnconfigure(self.frame_panel, 0)

        # body > display
        self.frame_display = TkFrame(self.frame_body, bg='royal blue')
        self.frame_display.grid(row=0, column=1, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_display, 0)
        self.set_all_grid_columnconfigure(self.frame_display, 0)

        # footer
        self.frame_footer = TkFrame(self.frame_root, bg='gray82')
        self.frame_footer.grid(row=2, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_footer, 0, 1)
        self.set_all_grid_columnconfigure(self.frame_footer, 0)

        # footer > panel setting
        self.frame_panel_setting = ttk.LabelFrame(self.frame_footer, text=u'輸入圖片選項: ', style='H4Padding.TLabelframe')
        self.frame_panel_setting.grid(row=0, column=0, sticky='news', pady=10)
        self.set_all_grid_rowconfigure(self.frame_panel_setting, 0, 1)
        self.set_all_grid_columnconfigure(self.frame_panel_setting, 0)

        # footer > panel setting > template option
        self.frame_template_options = TkFrame(self.frame_panel_setting, bg='gray82', pady=5)
        self.frame_template_options.grid(row=0, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_template_options, 0)
        self.set_all_grid_columnconfigure(self.frame_template_options, 0)

        # footer > panel setting > gamma
        self.frame_gamma = TkFrame(self.frame_panel_setting, bg='gray82', pady=5)
        self.frame_gamma.grid(row=1, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_gamma, 0)
        self.set_all_grid_columnconfigure(self.frame_gamma, 0)

        # footer > display setting
        self.frame_display_setting = ttk.LabelFrame(self.frame_footer, text=u'輸出圖片選項: ', style='H4Padding.TLabelframe')
        self.frame_display_setting.grid(row=1, column=0, sticky='news', pady=10)
        self.set_all_grid_rowconfigure(self.frame_display_setting, 0)
        self.set_all_grid_columnconfigure(self.frame_display_setting, 0)

        # footer > display setting > threshold options
        self.frame_threshold_options = TkFrame(self.frame_display_setting, bg='gray82', pady=5)
        self.frame_threshold_options.grid(row=0, column=0, sticky='news')

        # footer > display setting > manual threshold
        self.frame_manual_threshold = TkFrame(self.frame_display_setting, bg='gray82', pady=5)
        self.frame_manual_threshold.grid(row=1, column=0, sticky='news')
        self.set_all_grid_rowconfigure(self.frame_manual_threshold, 0)
        self.set_all_grid_columnconfigure(self.frame_manual_threshold, 0)

        self._init_widget_head()
        self._init_widget_body()
        self._init_widget_footer()

    # init head widget
    def _init_widget_head(self):
        self.set_all_grid_rowconfigure(self.frame_head, 0, 1)
        self.label_state = ttk.Label(self.frame_head, text=u'現在模式: N/A', style='H2.TLabel')
        self.label_state.grid(row=0, column=0, sticky='w')
        self.label_resize = ttk.Label(self.frame_head, text=u'原有尺寸 N/A-> 顯示尺寸 N/A', style='H2.TLabel')
        self.label_resize.grid(row=1, column=0, sticky='w')

    # init body widget
    def _init_widget_body(self):
        # panel
        self.set_all_grid_rowconfigure(self.frame_panel, 0, 1)
        self.label_panel = ttk.Label(self.frame_panel, text='Input Panel', style='H2.TLabel')
        self.label_panel.grid(row=0, column=0, sticky='ns')
        self.photo_panel = ImageNP.generate_checkboard((self._im_h, self._im_w), block_size=10)
        self.photo_panel = TkConverter.ndarray_to_photo(self.photo_panel)
        self.label_panel_image = ttk.Label(self.frame_panel, image=self.photo_panel)
        self.label_panel_image.grid(row=1, column=0, sticky='ns')

        # display
        self.label_display = ttk.Label(self.frame_display, text='Display', style='H2.TLabel')
        self.label_display.grid(row=0, column=0, columnspan=3)

        self.set_all_grid_rowconfigure(self.frame_display, 0, 1, 2)
        self.set_all_grid_columnconfigure(self.frame_display, 0, 1, 2)
        self.photo_small = ImageNP.generate_checkboard((self._im_h//2, self._im_w//3), 10)
        self.photo_small = TkConverter.ndarray_to_photo(self.photo_small)
        self.photo_large = ImageNP.generate_checkboard((self._im_h, self._im_w//3), 10)
        self.photo_large = TkConverter.ndarray_to_photo(self.photo_large)
        self.label_fl_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_fl_image.grid(row=1, column=0)
        self.label_fr_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_fr_image.grid(row=1, column=1)
        self.label_bl_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_bl_image.grid(row=2, column=0)
        self.label_br_image = ttk.Label(self.frame_display, image=self.photo_small)
        self.label_br_image.grid(row=2, column=1)
        self.label_body_image = ttk.Label(self.frame_display, image=self.photo_large)
        self.label_body_image.grid(row=1, column=2, rowspan=2)

    # init footer widget
    def _init_widget_footer(self):
        # input panel template option
        self.label_template = ttk.Label(self.frame_template_options, text=u'過濾樣式: ', style='H5.TLabel')
        self.label_template.grid(row=0, column=0, sticky='w')

        # input panel gamma
        self.label_gamma = ttk.Label(self.frame_gamma, text=u'調整對比 ({:.2f}): '.format(1.), style='H5.TLabel')
        self.label_gamma.grid(row=0, column=0, sticky='w')
        self.val_scale_gamma = tkinter.DoubleVar()
        self.val_scale_gamma.set(1.0)
        self.scale_gamma = ttk.Scale(self.frame_gamma,
                                     orient=tkinter.HORIZONTAL,
                                     length=self._im_w*2,
                                     from_=0, to=25,
                                     variable=self.val_scale_gamma,
                                     style='Gray.Horizontal.TScale')
        self.scale_gamma.state(('active', '!disabled'))
        self.scale_gamma.grid(row=0, column=1, sticky='w')

        # display threshold option
        self.label_threshold_options = ttk.Label(self.frame_threshold_options, text=u'門檻值選項: ', style='H5.TLabel')
        self.label_threshold_options.grid(row=0, column=0, sticky='w')
        self.val_threshold_option = tkinter.StringVar()
        self.val_threshold_option.set(THRESHOLD_OPTION[0][-1])
        self.radiobtn_threshold_options = []
        for i, op in enumerate(THRESHOLD_OPTION):
            text, val = op
            radiobtn = ttk.Radiobutton(self.frame_threshold_options,
                                       text=text,
                                       variable=self.val_threshold_option,
                                       value=val,
                                       style='H5.TRadiobutton')
            radiobtn.grid(row=0, column=i+1, sticky='w', padx=10)
            self.radiobtn_threshold_options.append(radiobtn)

        # display threshold manual scale
        self.label_manual_threshold = ttk.Label(self.frame_manual_threshold, text=u'門檻值 ({:.2f}): '.format(250), style='H5.TLabel')
        self.label_manual_threshold.grid(row=0, column=0, sticky='w')
        self.val_manual_threshold = tkinter.DoubleVar()
        self.val_manual_threshold.set(250)
        self.scale_manual_threshold = ttk.Scale(self.frame_manual_threshold,
                                                orient=tkinter.HORIZONTAL,
                                                length=self._im_w*2,
                                                from_=1, to=254,
                                                variable=self.val_manual_threshold,
                                                style='Gray.Horizontal.TScale')
        self.scale_manual_threshold.state(('active', '!disabled'))
        self.scale_manual_threshold.grid(row=0, column=1, sticky='news', columnspan=len(THRESHOLD_OPTION))

if __name__ == '__main__':
    logging.basicConfig(
        level=logging.INFO,
        format='%(asctime)s %(filename)12s:L%(lineno)3s [%(levelname)8s] %(message)s',
        datefmt='%Y-%m-%d %H:%M:%S',
        stream=sys.stdout
    )

    graphcut_viewer = GraphCutViewer()
    graphcut_viewer.mainloop()
<|endoftext|>


<commit_before># imports for tornado
import tornado
from tornado import web, httpserver, iploop

# imports for logging
import traceback
import os
from os import path

# imports for yara to work
import akka
import binascii
import ConfigParser
import sys
import yara

class YaraHandler(tornado.web.RequestHandler):
	@property
	def YaraEngine(self):
		if not self.application.engine:
			try:
				self.application.engine = yara.load(sys.argv[1])
			except Exception as e:
				print e
		return self.application.engine

class YaraProcess(YaraHandler):
	def process(self, tup, rule=None):
		try:
			if rules:
				rules = yara.load(rules)
				results = rules.match(tup)
			else:
				results = self.YaraEngine.match(tup)
			results2 = map(lambda x: {"rule": x.rule}, results)
			return results2
		except Exception, e:
			return e

	def get(self, filename):
		try:
			fullPath = os.path.join('/tmp/', filename)
			data = self.process(fullPath)
			self.write({"yara": data)
		except Exception as e:
			self.write({"error": traceback.format_exc(e)})

	def post(self, filename):
		try:
			fullPath = os.path.join('/tmp/', filename)
			custom_rule = self.files['custom_rule'][0]
			data = self.process(fullPath, custom_rule)
			self.write({"yara": data)
		except Exception as e:
			self.write({"error": traceback.format_exc(e)})

class Info(tornado.web.RequestHandler):
	# Emits a string which describes the purpose of the analytics
	def get(self):
		description = """
Provides Yara signature matching for samples using a collective
set of signatures or a provided custom signature.
Copyright 2015 Holmes Processing
		"""
		self.write(description)

class YaraApp(tornado.web.Application):
	def __init__(self):
		handlers = [
			(r'/', Info),
			(r'/yara/([a-zA-Z0-9\-]*)', YaraProcess),
		]
		settings = dict(
			template_path=path.join(path.dirname(__file__), 'templates'),
			static_path=path.join(path.dirname(__file__), 'static')
		)
		tornado.web.Application.__init__(self, handlers, **settings)
		self.engine = None

def main():
	server = tornado.httpserver.HTTPServer(YaraApp())
	server.listen(7701, address="127.0.0.1")
	tornado.ioloop.IOLoop.instance().start()

if __name__ == '__main__':
	main()<commit_msg>adjusted post from files to get_body_arguement<commit_after># imports for tornado
import tornado
from tornado import web, httpserver, iploop

# imports for logging
import traceback
import os
from os import path

# imports for yara to work
import akka
import binascii
import ConfigParser
import sys
import yara

class YaraHandler(tornado.web.RequestHandler):
	@property
	def YaraEngine(self):
		if not self.application.engine:
			try:
				self.application.engine = yara.load(sys.argv[1])
			except Exception as e:
				print e
		return self.application.engine

class YaraProcess(YaraHandler):
	def process(self, tup, rule=None):
		try:
			if rules:
				rules = yara.load(rules)
				results = rules.match(tup)
			else:
				results = self.YaraEngine.match(tup)
			results2 = map(lambda x: {"rule": x.rule}, results)
			return results2
		except Exception, e:
			return e

	def get(self, filename):
		try:
			fullPath = os.path.join('/tmp/', filename)
			data = self.process(fullPath)
			self.write({"yara": data)
		except Exception as e:
			self.write({"error": traceback.format_exc(e)})

	def post(self, filename):
		try:
			fullPath = os.path.join('/tmp/', filename)
			custom_rule = self.get_body_argument('custom_rule')
			data = self.process(fullPath, custom_rule)
			self.write({"yara": data)
		except Exception as e:
			self.write({"error": traceback.format_exc(e)})

class Info(tornado.web.RequestHandler):
	# Emits a string which describes the purpose of the analytics
	def get(self):
		description = """
Provides Yara signature matching for samples using a collective
set of signatures or a provided custom signature.
Copyright 2015 Holmes Processing
		"""
		self.write(description)

class YaraApp(tornado.web.Application):
	def __init__(self):
		handlers = [
			(r'/', Info),
			(r'/yara/([a-zA-Z0-9\-]*)', YaraProcess),
		]
		settings = dict(
			template_path=path.join(path.dirname(__file__), 'templates'),
			static_path=path.join(path.dirname(__file__), 'static')
		)
		tornado.web.Application.__init__(self, handlers, **settings)
		self.engine = None

def main():
	server = tornado.httpserver.HTTPServer(YaraApp())
	server.listen(7701, address="127.0.0.1")
	tornado.ioloop.IOLoop.instance().start()

if __name__ == '__main__':
	main()<|endoftext|>


<commit_before>#
# Copyright (C) 2018 - 2021 Satoru SATOH <satoru.satoh @ gmail.com>
# SPDX-License-Identifier: MIT
#
# pylint: disable=unidiomatic-typecheck
r"""Abstract processor module.

.. versionadded:: 0.9.5

   - Add to abstract processors such like Parsers (loaders and dumpers).
"""
import operator
import typing
import warnings

import pkg_resources

import anyconfig.ioinfo
import anyconfig.models.processor
import anyconfig.utils

from anyconfig.globals import (
    UnknownProcessorTypeError, UnknownFileTypeError, IOInfo
)

ProcT = typing.TypeVar('ProcT', bound=anyconfig.models.processor.Processor)
ProcClsT = typing.Type[ProcT]

def load_plugins(pgroup: str) -> typing.Iterator[ProcClsT]:
    """
    A generator function to yield a class object of
    :class:`anyconfig.models.processor.Processor`.

    :param pgroup: A string represents plugin type, e.g. anyconfig_backends
    """
    for res in pkg_resources.iter_entry_points(pgroup):
        try:
            yield res.load()
        except ImportError as exc:
            warnings.warn(f'Failed to load plugin, exc={exc!s}')

def sort_by_prio(prs):
    """
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: Sambe as above but sorted by priority
    """
    return sorted(prs, key=operator.methodcaller("priority"), reverse=True)

def select_by_key(items, sort_fn=sorted):
    """
    :param items: A list of tuples of keys and values, [([key], val)]
    :return: A list of tuples of key and values, [(key, [val])]

    >>> select_by_key([(["a", "aaa"], 1), (["b", "bb"], 2), (["a"], 3)])
    [('a', [1, 3]), ('aaa', [1]), ('b', [2]), ('bb', [2])]
    """
    itr = anyconfig.utils.concat(((k, v) for k in ks) for ks, v in items)
    return list((k, sort_fn(t[1] for t in g))
                for k, g
                in anyconfig.utils.groupby(itr, operator.itemgetter(0)))

def list_by_x(prs, key):
    """
    :param key: Grouping key, "type" or "extensions"
    :return:
        A list of :class:`Processor` or its children classes grouped by
        given 'item', [(cid, [:class:`Processor`)]] by default
    """
    if key == "type":
        kfn = operator.methodcaller(key)
        res = sorted(((k, sort_by_prio(g)) for k, g
                      in anyconfig.utils.groupby(prs, kfn)),
                     key=operator.itemgetter(0))

    elif key == "extensions":
        res = select_by_key(((p.extensions(), p) for p in prs),
                            sort_fn=sort_by_prio)
    else:
        raise ValueError("Argument 'key' must be 'type' or "
                         "'extensions' but it was '%s'" % key)

    return res

def findall_with_pred(predicate, prs):
    """
    :param predicate: any callable to filter results
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: A list of appropriate processor classes or []
    """
    return sorted((p for p in prs if predicate(p)),
                  key=operator.methodcaller("priority"), reverse=True)

def maybe_processor(type_or_id, cls=anyconfig.models.processor.Processor):
    """
    :param type_or_id:
        Type of the data to process or ID of the processor class or
        :class:`anyconfig.models.processor.Processor` class object or its
        instance
    :param cls: A class object to compare with 'type_or_id'
    :return: Processor instance or None
    """
    if isinstance(type_or_id, cls):
        return type_or_id

    if type(type_or_id) == type(cls) and issubclass(type_or_id, cls):
        return type_or_id()

    return None

def find_by_type_or_id(type_or_id, prs):
    """
    :param type_or_id: Type of the data to process or ID of the processor class
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return:
        A list of processor classes to process files of given data type or
        processor 'type_or_id' found by its ID
    :raises: UnknownProcessorTypeError
    """
    def pred(pcls):
        """Predicate"""
        return pcls.cid() == type_or_id or pcls.type() == type_or_id

    pclss = findall_with_pred(pred, prs)
    if not pclss:
        raise UnknownProcessorTypeError(type_or_id)

    return pclss

def find_by_fileext(fileext, prs):
    """
    :param fileext: File extension
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: A list of processor class to processor files with given extension
    :raises: UnknownFileTypeError
    """
    def pred(pcls):
        """Predicate"""
        return fileext in pcls.extensions()

    pclss = findall_with_pred(pred, prs)
    if not pclss:
        raise UnknownFileTypeError("file extension={}".format(fileext))

    return pclss  # :: [Processor], never []

def find_by_maybe_file(obj, prs):
    """
    :param obj:
        a file path, file or file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo' (namedtuple) object
    :param cps_by_ext: A list of processor classes
    :return: A list of processor classes to process given (maybe) file
    :raises: UnknownFileTypeError
    """
    if not isinstance(obj, IOInfo):
        obj = anyconfig.ioinfo.make(obj)

    return find_by_fileext(obj.extension, prs)  # :: [Processor], never []

# pylint: disable=unused-argument
def findall(obj, prs, forced_type=None,
            cls=anyconfig.models.processor.Processor):
    """
    :param obj:
        a file path, file, file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo` (namedtuple) object
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :param forced_type:
        Forced processor type of the data to process or ID of the processor
        class or None
    :param cls: A class object to compare with 'forced_type' later

    :return: A list of instances of processor classes to process 'obj' data
    :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
    """
    if (obj is None or not obj) and forced_type is None:
        raise ValueError("The first argument 'obj' or the second argument "
                         "'forced_type' must be something other than "
                         "None or False.")

    if forced_type is None:
        pclss = find_by_maybe_file(obj, prs)  # :: [Processor], never []
    else:
        pclss = find_by_type_or_id(forced_type, prs)  # Do.

    return pclss

def find(obj, prs, forced_type=None, cls=anyconfig.models.processor.Processor):
    """
    :param obj:
        a file path, file, file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo' (namedtuple) object
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :param forced_type:
        Forced processor type of the data to process or ID of the processor
        class or :class:`anyconfig.models.processor.Processor` class object or
        its instance itself
    :param cls: A class object to compare with 'forced_type' later

    :return: an instance of processor class to process 'obj' data
    :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
    """
    if forced_type is not None:
        processor = maybe_processor(forced_type, cls=cls)
        if processor is not None:
            return processor

    pclss = findall(obj, prs, forced_type=forced_type, cls=cls)
    return pclss[0]()

class Processors:
    """An abstract class of which instance holding processors.
    """
    _pgroup: str = ''  # processor group name to load plugins

    def __init__(self, processors=None):
        """
        :param processors:
            A list of :class:`Processor` or its children class objects or None
        """
        self._processors = dict()  # {<processor_class_id>: <processor_class>}
        if processors is not None:
            for pcls in processors:
                self.register(pcls)

        self.load_plugins()

    def register(self, pcls):
        """
        :param pclss: :class:`Processor` or its children classes
        """
        if pcls.cid() not in self._processors:
            self._processors[pcls.cid()] = pcls

    def load_plugins(self):
        """Load and register pluggable processor classes internally.
        """
        if self._pgroup:
            for pcls in load_plugins(self._pgroup):
                self.register(pcls)

    def list(self, sort=False):
        """
        :param sort: Result will be sorted if it's True
        :return: A list of :class:`Processor` or its children classes
        """
        prs = self._processors.values()
        if sort:
            return sorted(prs, key=operator.methodcaller("cid"))

        return prs

    def list_by_cid(self):
        """
        :return:
            A list of :class:`Processor` or its children classes grouped by
            each cid, [(cid, [:class:`Processor`)]]
        """
        prs = self._processors
        return sorted(((cid, [prs[cid]]) for cid in sorted(prs.keys())),
                      key=operator.itemgetter(0))

    def list_by_type(self):
        """
        :return:
            A list of :class:`Processor` or its children classes grouped by
            each type, [(type, [:class:`Processor`)]]
        """
        return list_by_x(self.list(), "type")

    def list_by_x(self, item=None):
        """
        :param item: Grouping key, one of "cid", "type" and "extensions"
        :return:
            A list of :class:`Processor` or its children classes grouped by
            given 'item', [(cid, [:class:`Processor`)]] by default
        """
        prs = self._processors

        if item is None or item == "cid":  # Default.
            res = [(cid, [prs[cid]]) for cid in sorted(prs.keys())]

        elif item in ("type", "extensions"):
            res = list_by_x(prs.values(), item)
        else:
            raise ValueError("keyword argument 'item' must be one of "
                             "None, 'cid', 'type' and 'extensions' "
                             "but it was '%s'" % item)
        return res

    def list_x(self, key=None):
        """
        :param key: Which of key to return from "cid", "type", and "extention"
        :return: A list of x 'key'
        """
        if key in ("cid", "type"):
            return sorted(set(operator.methodcaller(key)(p)
                              for p in self._processors.values()))
        if key == "extension":
            return sorted(k for k, _v in self.list_by_x("extensions"))

        raise ValueError("keyword argument 'key' must be one of "
                         "None, 'cid', 'type' and 'extension' "
                         "but it was '%s'" % key)

    def findall(self, obj, forced_type=None,
                cls=anyconfig.models.processor.Processor):
        """
        :param obj:
            a file path, file, file-like object, pathlib.Path object or an
            'anyconfig.globals.IOInfo' (namedtuple) object
        :param forced_type: Forced processor type to find
        :param cls: A class object to compare with 'ptype'

        :return: A list of instances of processor classes to process 'obj'
        :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
        """
        return [p() for p in findall(obj, self.list(),
                                     forced_type=forced_type, cls=cls)]

    def find(self, obj, forced_type=None,
             cls=anyconfig.models.processor.Processor):
        """
        :param obj:
            a file path, file, file-like object, pathlib.Path object or an
            'anyconfig.globals.IOInfo' (namedtuple) object
        :param forced_type: Forced processor type to find
        :param cls: A class object to compare with 'ptype'

        :return: an instance of processor class to process 'obj'
        :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
        """
        return find(obj, self.list(), forced_type=forced_type, cls=cls)

# vim:sw=4:ts=4:et:
<commit_msg>change: add type hints to anyconfig.processors along with some refactorings and fixes<commit_after>#
# Copyright (C) 2018 - 2021 Satoru SATOH <satoru.satoh @ gmail.com>
# SPDX-License-Identifier: MIT
#
# pylint: disable=unidiomatic-typecheck
r"""Abstract processor module.

.. versionadded:: 0.9.5

   - Add to abstract processors such like Parsers (loaders and dumpers).
"""
import operator
import typing
import warnings

import pkg_resources

import anyconfig.ioinfo
import anyconfig.models.processor
import anyconfig.utils

from anyconfig.globals import (
    UnknownProcessorTypeError, UnknownFileTypeError, IOInfo,
    PathOrIOInfoT
)

ProcT = typing.TypeVar('ProcT', bound=anyconfig.models.processor.Processor)
ProcClsT = typing.Type[ProcT]
ProcClssT = typing.List[ProcClsT]

def load_plugins(pgroup: str) -> typing.Iterator[ProcClsT]:
    """
    A generator function to yield a class object of
    :class:`anyconfig.models.processor.Processor`.

    :param pgroup: A string represents plugin type, e.g. anyconfig_backends
    """
    for res in pkg_resources.iter_entry_points(pgroup):
        try:
            yield res.load()
        except ImportError as exc:
            warnings.warn(f'Failed to load plugin, exc={exc!s}')

def sort_by_prio(prs: ProcClssT) -> ProcClssT:
    """
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: Sambe as above but sorted by priority
    """
    return sorted(prs, key=operator.methodcaller("priority"), reverse=True)

def select_by_key(items: typing.Iterable[
                    typing.Tuple[typing.List[str], typing.Any]
                  ],
                  sort_fn: typing.Callable[..., typing.Any] = sorted
                  ) -> typing.List[typing.Tuple[str, typing.List[typing.Any]]]:
    """
    :param items: A list of tuples of keys and values, [([key], val)]
    :return: A list of tuples of key and values, [(key, [val])]

    >>> select_by_key([(["a", "aaa"], 1), (["b", "bb"], 2), (["a"], 3)])
    [('a', [1, 3]), ('aaa', [1]), ('b', [2]), ('bb', [2])]
    """
    itr = anyconfig.utils.concat(((k, v) for k in ks) for ks, v in items)
    return list((k, sort_fn(t[1] for t in g))
                for k, g
                in anyconfig.utils.groupby(itr, operator.itemgetter(0)))

def list_by_x(prs: typing.Iterable[ProcClsT], key: str
              ) -> typing.List[typing.Tuple[str, ProcClssT]]:
    """
    :param key: Grouping key, "type" or "extensions"
    :return:
        A list of :class:`Processor` or its children classes grouped by
        given 'item', [(cid, [:class:`Processor`)]] by default
    """
    if key == "type":
        kfn = operator.methodcaller(key)
        res = sorted(((k, sort_by_prio(g)) for k, g
                      in anyconfig.utils.groupby(prs, kfn)),
                     key=operator.itemgetter(0))

    elif key == "extensions":
        res: typing.List[  # type: ignore
            typing.Tuple[str, ProcClssT]
        ] = select_by_key(((p.extensions(), p) for p in prs),
                          sort_fn=sort_by_prio)
    else:
        raise ValueError("Argument 'key' must be 'type' or "
                         "'extensions' but it was '%s'" % key)

    return res

def findall_with_pred(predicate: typing.Callable[..., bool],
                      prs: ProcClssT) -> ProcClssT:
    """
    :param predicate: any callable to filter results
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: A list of appropriate processor classes or []
    """
    return sorted((p for p in prs if predicate(p)),
                  key=operator.methodcaller("priority"), reverse=True)

def maybe_processor(type_or_id: typing.Union[ProcT, ProcClsT],
                    cls: ProcClsT = anyconfig.models.processor.Processor
                    ) -> typing.Optional[ProcT]:
    """
    :param type_or_id:
        Type of the data to process or ID of the processor class or
        :class:`anyconfig.models.processor.Processor` class object or its
        instance
    :param cls: A class object to compare with 'type_or_id'
    :return: Processor instance or None
    """
    if isinstance(type_or_id, cls):
        return type_or_id

    if type(type_or_id) == type(cls):
        if issubclass(typing.cast(ProcClsT, type_or_id), cls):
            return type_or_id()  # type: ignore

    return None

def find_by_type_or_id(type_or_id: str, prs: ProcClssT) -> ProcClssT:
    """
    :param type_or_id: Type of the data to process or ID of the processor class
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return:
        A list of processor classes to process files of given data type or
        processor 'type_or_id' found by its ID
    :raises: UnknownProcessorTypeError
    """
    def pred(pcls):
        """Predicate"""
        return pcls.cid() == type_or_id or pcls.type() == type_or_id

    pclss = findall_with_pred(pred, prs)
    if not pclss:
        raise UnknownProcessorTypeError(type_or_id)

    return pclss

def find_by_fileext(fileext: str, prs: ProcClssT) -> ProcClssT:
    """
    :param fileext: File extension
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :return: A list of processor class to processor files with given extension
    :raises: UnknownFileTypeError
    """
    def pred(pcls):
        """Predicate"""
        return fileext in pcls.extensions()

    pclss = findall_with_pred(pred, prs)
    if not pclss:
        raise UnknownFileTypeError("file extension={}".format(fileext))

    return pclss  # :: [Processor], never []

def find_by_maybe_file(obj: PathOrIOInfoT, prs: ProcClssT) -> ProcClssT:
    """
    :param obj:
        a file path, file or file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo' (namedtuple) object
    :param cps_by_ext: A list of processor classes
    :return: A list of processor classes to process given (maybe) file
    :raises: UnknownFileTypeError
    """
    if not isinstance(obj, IOInfo):
        obj = anyconfig.ioinfo.make(obj)

    return find_by_fileext(obj.extension, prs)  # :: [Processor], never []

# pylint: disable=unused-argument
def findall(obj: PathOrIOInfoT, prs: ProcClssT,
            forced_type: typing.Optional[str] = None,
            cls: ProcClsT = anyconfig.models.processor.Processor
            ) -> ProcClssT:
    """
    :param obj:
        a file path, file, file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo` (namedtuple) object
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :param forced_type:
        Forced processor type of the data to process or ID of the processor
        class or None
    :param cls: A class object to compare with 'forced_type' later

    :return: A list of instances of processor classes to process 'obj' data
    :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
    """
    if (obj is None or not obj) and forced_type is None:
        raise ValueError("The first argument 'obj' or the second argument "
                         "'forced_type' must be something other than "
                         "None or False.")

    if forced_type is None:
        pclss = find_by_maybe_file(obj, prs)  # :: [Processor], never []
    else:
        pclss = find_by_type_or_id(forced_type, prs)  # Do.

    return pclss

def find(obj: PathOrIOInfoT, prs: ProcClssT,
         forced_type: typing.Union[ProcClsT, ProcClssT, str, None] = None,
         cls: ProcClsT = anyconfig.models.processor.Processor
         ) -> ProcT:
    """
    :param obj:
        a file path, file, file-like object, pathlib.Path object or an
        'anyconfig.globals.IOInfo' (namedtuple) object
    :param prs: A list of :class:`anyconfig.models.processor.Processor` classes
    :param forced_type:
        Forced processor type of the data to process or ID of the processor
        class or :class:`anyconfig.models.processor.Processor` class object or
        its instance itself
    :param cls: A class object to compare with 'forced_type' later

    :return: an instance of processor class to process 'obj' data
    :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
    """
    if forced_type is not None and not isinstance(forced_type, str):
        processor = maybe_processor(
            typing.cast(typing.Union[ProcT, ProcClsT], forced_type), cls=cls
        )
        if processor is not None:
            return processor

    pclss = findall(
        obj, prs, forced_type=typing.cast(str, forced_type), cls=cls
    )
    return pclss[0]()

class Processors:
    """An abstract class of which instance holding processors.
    """
    _pgroup: str = ''  # processor group name to load plugins

    def __init__(self, processors: ProcClssT = None) -> None:
        """
        :param processors:
            A list of :class:`Processor` or its children class objects or None
        """
        # {<processor_class_id>: <processor_class>}
        self._processors: typing.Dict[str, ProcClsT] = dict()
        if processors is not None:
            for pcls in processors:
                self.register(pcls)

        self.load_plugins()

    def register(self, pcls: ProcClsT) -> None:
        """
        :param pclss: :class:`Processor` or its children classes
        """
        if pcls.cid() not in self._processors:
            self._processors[pcls.cid()] = pcls

    def load_plugins(self) -> None:
        """Load and register pluggable processor classes internally.
        """
        if self._pgroup:
            for pcls in load_plugins(self._pgroup):
                self.register(pcls)

    def list(self, sort: bool = False) -> ProcClssT:
        """
        :param sort: Result will be sorted if it's True
        :return: A list of :class:`Processor` or its children classes
        """
        prs = self._processors.values()
        if sort:
            return sorted(prs, key=operator.methodcaller("cid"))

        return list(prs)

    def list_by_cid(self) -> typing.List[typing.Tuple[str, ProcClssT]]:
        """
        :return:
            A list of :class:`Processor` or its children classes grouped by
            each cid, [(cid, [:class:`Processor`)]]
        """
        prs = self._processors
        return sorted(((cid, [prs[cid]]) for cid in sorted(prs.keys())),
                      key=operator.itemgetter(0))

    def list_by_type(self) -> typing.List[typing.Tuple[str, ProcClssT]]:
        """
        :return:
            A list of :class:`Processor` or its children classes grouped by
            each type, [(type, [:class:`Processor`)]]
        """
        return list_by_x(self.list(), "type")

    def list_by_x(self, item: typing.Optional[str] = None
                  ) -> typing.List[typing.Tuple[str, ProcClssT]]:
        """
        :param item: Grouping key, one of "cid", "type" and "extensions"
        :return:
            A list of :class:`Processor` or its children classes grouped by
            given 'item', [(cid, [:class:`Processor`)]] by default
        """
        prs = self._processors

        if item is None or item == "cid":  # Default.
            res = [(cid, [prs[cid]]) for cid in sorted(prs.keys())]

        elif item in ("type", "extensions"):
            res = list_by_x(prs.values(), typing.cast(str, item))
        else:
            raise ValueError("keyword argument 'item' must be one of "
                             "None, 'cid', 'type' and 'extensions' "
                             "but it was '%s'" % item)
        return res

    def list_x(self, key: typing.Optional[str] = None) -> typing.List[str]:
        """
        :param key: Which of key to return from "cid", "type", and "extention"
        :return: A list of x 'key'
        """
        if key in ("cid", "type"):
            return sorted(set(operator.methodcaller(key)(p)
                              for p in self._processors.values()))
        if key == "extension":
            return sorted(k for k, _v in self.list_by_x("extensions"))

        raise ValueError("keyword argument 'key' must be one of "
                         "None, 'cid', 'type' and 'extension' "
                         "but it was '%s'" % key)

    def findall(self, obj: PathOrIOInfoT,
                forced_type: typing.Optional[str] = None,
                cls: ProcClsT = anyconfig.models.processor.Processor
                ) -> typing.List[ProcT]:
        """
        :param obj:
            a file path, file, file-like object, pathlib.Path object or an
            'anyconfig.globals.IOInfo' (namedtuple) object
        :param forced_type: Forced processor type to find
        :param cls: A class object to compare with 'ptype'

        :return: A list of instances of processor classes to process 'obj'
        :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
        """
        return [p() for p in findall(obj, self.list(),
                                     forced_type=forced_type, cls=cls)]

    def find(self, obj: PathOrIOInfoT,
             forced_type: typing.Optional[str] = None,
             cls: ProcClsT = anyconfig.models.processor.Processor
             ) -> typing.List[ProcT]:
        """
        :param obj:
            a file path, file, file-like object, pathlib.Path object or an
            'anyconfig.globals.IOInfo' (namedtuple) object
        :param forced_type: Forced processor type to find
        :param cls: A class object to compare with 'ptype'

        :return: an instance of processor class to process 'obj'
        :raises: ValueError, UnknownProcessorTypeError, UnknownFileTypeError
        """
        return find(obj, self.list(), forced_type=forced_type, cls=cls)

# vim:sw=4:ts=4:et:
<|endoftext|>


<commit_before>from math import log

from app.dataformats import peptable as peptabledata
from app.dataformats import prottable as prottabledata
from app.dataformats import mzidtsv as mzidtsvdata

from app.actions.psmtopeptable import evaluate_peptide
from app.readers import fasta

def generate_bestpep_proteins(peptides, scorecol, minlog, outputaccfield,
        protcol, higherbetter=True):
    """Best peptide for each protein in a table"""
    protein_peptides = {}
    if minlog:
        higherbetter = False
    if not protcol:
        protcol = peptabledata.HEADER_MASTERPROTEINS
    for peptide in peptides:
        p_acc = peptide[protcol]
        if ';' in p_acc:
            continue
        protein_peptides = evaluate_peptide(protein_peptides, peptide, p_acc,
                higherbetter, scorecol, fncol=False)
    if minlog:
        try:
            nextbestscore = min([pep['score'] for pep in
                                 protein_peptides.values()
                                 if pep['score'] > 0])
        except ValueError:
            print('WARNING: Cannot find score of type {} which is above 0. '
                    'Only scores above zero can have a -log value.'.format(scorecol))
            nextbestscore = -log(1e-06, 10) # 1 decoy in a million, fake value
        else:
            nextbestscore = -log(nextbestscore, 10)
    for protein, peptide in protein_peptides.items():
        if minlog and peptide['score'] != 'NA':
            peptide['score'] = log_score(peptide['score'], nextbestscore)
        yield {
                outputaccfield: protein,
                prottabledata.HEADER_QSCORE: str(peptide['score'])
                }

def log_score(score, nextbestscore):
    try:
        logged_score = -log(score, 10)
    except ValueError:
        logged_score = nextbestscore + 2
    return logged_score

def generate_protein_fdr(target, decoy, featfield):
    tproteins = [x for x in target if get_score(x) is not None]
    dproteins = [x for x in decoy if get_score(x) is not None]
    [x.update({'target_decoy': 'target'}) for x in tproteins]
    [x.update({'target_decoy': 'decoy'}) for x in dproteins]
    proteins = sorted(tproteins + dproteins, key=lambda x: get_score(x),
                      reverse=True)
    #fdrheader = headerfields['proteinfdr'][prottabledata.HEADER_QVAL][None]
    # FIXME complex stupid headerfields system, remove it?
    fdrheader = prottabledata.HEADER_QVAL
    for protein in qvalue_generator(fdrheader, proteins, featfield):
        yield protein

def generate_pick_fdr(target, decoy, tfastafn, dfastafn, picktype, featfield,
        fastadelim, genefield):
    t_scores, d_scores = {}, {}
    for protein in target:
        acc = protein[featfield]
        t_scores[acc] = protein
        t_scores[acc]['target_decoy'] = 'target'
    for protein in decoy:
        acc = protein[featfield]
        d_scores[acc] = protein
        d_scores[acc]['target_decoy'] = 'decoy'
    if picktype == 'fasta':
        prefixlen = len(mzidtsvdata.DECOY_PREFIX)
        genetype = 'genename' if featfield == mzidtsvdata.HEADER_SYMBOL else 'ensg'
        tfasta = fasta.get_genes_pickfdr(tfastafn, genetype, fastadelim, genefield)
        dfasta = fasta.get_genes_pickfdr(dfastafn, genetype, fastadelim, genefield)
        tdmap = {}
        for target, decoy in zip(tfasta, dfasta):
            tdmap[target] = decoy
    elif picktype == 'result':
        # FIXME this code path is not accessed but kept  in case we ever need
        # to get back to result picktype. See commit 58080fdc11d0800117a09122d66173e7ac54292a
        tdmap = {}
        for dprot in d_scores:
            faketprot = dprot.replace(mzidtsvdata.DECOY_PREFIX, '')
            tdmap[faketprot] = dprot
        for tprot in t_scores:
            if not tdmap.get(tprot, False):
                tdmap[tprot] = None
    picked_proteins = []
    for tgene, dgene in tdmap.items():
        picked = pick_target_decoy(t_scores.get(tgene), d_scores.get(dgene))
        if picked:
            picked_proteins.append(picked)
    sorted_proteins = sorted(picked_proteins, key=lambda x: get_score(x),
                             reverse=True)
    #fdrheader = headerfields['proteinfdr'][prottabledata.HEADER_QVAL][None]
    fdrheader = prottabledata.HEADER_QVAL
    for protein in qvalue_generator(fdrheader, sorted_proteins, featfield):
        yield protein

def qvalue_generator(fdrheader, sorted_features, featfield):
    tdcounter = {'target': 0, 'decoy': 0}
    previousscore = get_score(sorted_features[0])
    outfeats = []
    for feat in sorted_features:
        outfeat = {k: v for k, v in feat.items()}
        score = get_score(outfeat)
        if score != previousscore:
            # new score, all proteins with previous score get same fdr
            previousscore = score
            try:
                fdr = tdcounter['decoy'] / float(tdcounter['target'])
            except ZeroDivisionError:
                fdr = 1
            for feat in sorted(outfeats, key=lambda x: x[featfield]):
                feat[fdrheader] = fdr
                yield feat
            outfeats = []
        tdcounter[outfeat['target_decoy']] += 1
        # Only report target hits so FDR=D/T
        if outfeat['target_decoy'] == 'target':
            outfeats.append(outfeat)
    # All proteins from bottom of list (no new score) get FDR as well
    try:
        fdr = tdcounter['decoy'] / float(tdcounter['target'])
    except ZeroDivisionError:
        fdr = 1
    for feat in sorted(outfeats, key=lambda x: x[featfield]):
        feat[fdrheader] = fdr
        yield feat

def get_score(protein):
    try:
        return float(protein[prottabledata.HEADER_QSCORE])
    except (TypeError, ValueError):
        return None

def pick_target_decoy(tfeature, dfeature):
    """Feed it with a target and decoy score and the protein/gene/id names,
    and this will return target/decoy type, the winning ID and the score"""
    tscore, dscore = get_score(tfeature), get_score(dfeature)
    if tscore is None and dscore is None:
        return False
    elif None in [tscore, dscore]:
        # return the non-False feature
        return [v for k, v in {tscore: tfeature, dscore: dfeature}.items()
                if k is not False][0]
    elif tscore == dscore:
        # same score or both False
        return False
    elif tscore > dscore:
        return tfeature
    elif tscore < dscore:
        return dfeature
    else:
        # in case uncaught edgecase occurs
        print('WARNING, target score {} and decoy score {} could not be '
              'compared'.format(tscore, dscore))
        return False

def generate_top_ms1_peptides(peptides, protcol):
    """Fed with a peptides generator, this returns the 3 PSMs with
    the highest precursor intensities (or areas, or whatever is
    given in the HEADER_PRECURSOR_QUANT"""
    top_ms1_peps = {}
    for peptide in peptides:
        protacc = peptide[protcol]
        precursor_amount = peptide[peptabledata.HEADER_AREA]
        if ';' in protacc or precursor_amount == 'NA':
            continue
        precursor_amount = float(precursor_amount)
        pepseq = peptide[peptabledata.HEADER_PEPTIDE]
        try:
            peptide_area = top_ms1_peps[protacc][pepseq]
        except KeyError:
            try:
                top_ms1_peps[protacc][pepseq] = precursor_amount
            except KeyError:
                top_ms1_peps[protacc] = {pepseq: precursor_amount}
        else:
            if precursor_amount > peptide_area:
                top_ms1_peps[protacc][pepseq] = precursor_amount
    return top_ms1_peps

def calculate_protein_precursor_quant(top_ms1_peps, prot_acc):
    try:
        amounts = top_ms1_peps[prot_acc].values()
    except KeyError:
        return 'NA'
    else:
        amounts = sorted([x for x in amounts if x > 0], reverse=True)[:3]
        return sum(amounts) / len(amounts)

def add_ms1_quant_from_top3_mzidtsv(features, peptides, outputaccfield, featcol):
    """Collects PSMs with the highest precursor quant values,
    adds sum of the top 3 of these to a protein table"""
    top_ms1_peps = generate_top_ms1_peptides(peptides, featcol)
    for feat in features:
        acc = feat[outputaccfield]
        prec_area = calculate_protein_precursor_quant(top_ms1_peps, acc)
        outfeat = {k: v for k, v in feat.items()}
        outfeat[prottabledata.HEADER_AREA] = str(prec_area)
#        outprotein[headerfields['precursorquant'][
#            prottabledata.HEADER_AREA][None]] = str(prec_area)
        yield outfeat
<commit_msg>Skip NA as accession when creating gene/protein tables<commit_after>from math import log

from app.dataformats import peptable as peptabledata
from app.dataformats import prottable as prottabledata
from app.dataformats import mzidtsv as mzidtsvdata

from app.actions.psmtopeptable import evaluate_peptide
from app.readers import fasta

def generate_bestpep_proteins(peptides, scorecol, minlog, outputaccfield,
        protcol, higherbetter=True):
    """Best peptide for each protein in a table"""
    protein_peptides = {}
    if minlog:
        higherbetter = False
    if not protcol:
        protcol = peptabledata.HEADER_MASTERPROTEINS
    for peptide in peptides:
        p_acc = peptide[protcol]
        if ';' in p_acc or p_acc == 'NA':
            continue
        protein_peptides = evaluate_peptide(protein_peptides, peptide, p_acc,
                higherbetter, scorecol, fncol=False)
    if minlog:
        try:
            nextbestscore = min([pep['score'] for pep in
                                 protein_peptides.values()
                                 if pep['score'] > 0])
        except ValueError:
            print('WARNING: Cannot find score of type {} which is above 0. '
                    'Only scores above zero can have a -log value.'.format(scorecol))
            nextbestscore = -log(1e-06, 10) # 1 decoy in a million, fake value
        else:
            nextbestscore = -log(nextbestscore, 10)
    for protein, peptide in protein_peptides.items():
        if minlog and peptide['score'] != 'NA':
            peptide['score'] = log_score(peptide['score'], nextbestscore)
        yield {
                outputaccfield: protein,
                prottabledata.HEADER_QSCORE: str(peptide['score'])
                }

def log_score(score, nextbestscore):
    try:
        logged_score = -log(score, 10)
    except ValueError:
        logged_score = nextbestscore + 2
    return logged_score

def generate_protein_fdr(target, decoy, featfield):
    tproteins = [x for x in target if get_score(x) is not None]
    dproteins = [x for x in decoy if get_score(x) is not None]
    [x.update({'target_decoy': 'target'}) for x in tproteins]
    [x.update({'target_decoy': 'decoy'}) for x in dproteins]
    proteins = sorted(tproteins + dproteins, key=lambda x: get_score(x),
                      reverse=True)
    #fdrheader = headerfields['proteinfdr'][prottabledata.HEADER_QVAL][None]
    # FIXME complex stupid headerfields system, remove it?
    fdrheader = prottabledata.HEADER_QVAL
    for protein in qvalue_generator(fdrheader, proteins, featfield):
        yield protein

def generate_pick_fdr(target, decoy, tfastafn, dfastafn, picktype, featfield,
        fastadelim, genefield):
    t_scores, d_scores = {}, {}
    for protein in target:
        acc = protein[featfield]
        t_scores[acc] = protein
        t_scores[acc]['target_decoy'] = 'target'
    for protein in decoy:
        acc = protein[featfield]
        d_scores[acc] = protein
        d_scores[acc]['target_decoy'] = 'decoy'
    if picktype == 'fasta':
        prefixlen = len(mzidtsvdata.DECOY_PREFIX)
        genetype = 'genename' if featfield == mzidtsvdata.HEADER_SYMBOL else 'ensg'
        tfasta = fasta.get_genes_pickfdr(tfastafn, genetype, fastadelim, genefield)
        dfasta = fasta.get_genes_pickfdr(dfastafn, genetype, fastadelim, genefield)
        tdmap = {}
        for target, decoy in zip(tfasta, dfasta):
            tdmap[target] = decoy
    elif picktype == 'result':
        # FIXME this code path is not accessed but kept  in case we ever need
        # to get back to result picktype. See commit 58080fdc11d0800117a09122d66173e7ac54292a
        tdmap = {}
        for dprot in d_scores:
            faketprot = dprot.replace(mzidtsvdata.DECOY_PREFIX, '')
            tdmap[faketprot] = dprot
        for tprot in t_scores:
            if not tdmap.get(tprot, False):
                tdmap[tprot] = None
    picked_proteins = []
    for tgene, dgene in tdmap.items():
        picked = pick_target_decoy(t_scores.get(tgene), d_scores.get(dgene))
        if picked:
            picked_proteins.append(picked)
    sorted_proteins = sorted(picked_proteins, key=lambda x: get_score(x),
                             reverse=True)
    #fdrheader = headerfields['proteinfdr'][prottabledata.HEADER_QVAL][None]
    fdrheader = prottabledata.HEADER_QVAL
    for protein in qvalue_generator(fdrheader, sorted_proteins, featfield):
        yield protein

def qvalue_generator(fdrheader, sorted_features, featfield):
    tdcounter = {'target': 0, 'decoy': 0}
    previousscore = get_score(sorted_features[0])
    outfeats = []
    for feat in sorted_features:
        outfeat = {k: v for k, v in feat.items()}
        score = get_score(outfeat)
        if score != previousscore:
            # new score, all proteins with previous score get same fdr
            previousscore = score
            try:
                fdr = tdcounter['decoy'] / float(tdcounter['target'])
            except ZeroDivisionError:
                fdr = 1
            for feat in sorted(outfeats, key=lambda x: x[featfield]):
                feat[fdrheader] = fdr
                yield feat
            outfeats = []
        tdcounter[outfeat['target_decoy']] += 1
        # Only report target hits so FDR=D/T
        if outfeat['target_decoy'] == 'target':
            outfeats.append(outfeat)
    # All proteins from bottom of list (no new score) get FDR as well
    try:
        fdr = tdcounter['decoy'] / float(tdcounter['target'])
    except ZeroDivisionError:
        fdr = 1
    for feat in sorted(outfeats, key=lambda x: x[featfield]):
        feat[fdrheader] = fdr
        yield feat

def get_score(protein):
    try:
        return float(protein[prottabledata.HEADER_QSCORE])
    except (TypeError, ValueError):
        return None

def pick_target_decoy(tfeature, dfeature):
    """Feed it with a target and decoy score and the protein/gene/id names,
    and this will return target/decoy type, the winning ID and the score"""
    tscore, dscore = get_score(tfeature), get_score(dfeature)
    if tscore is None and dscore is None:
        return False
    elif None in [tscore, dscore]:
        # return the non-False feature
        return [v for k, v in {tscore: tfeature, dscore: dfeature}.items()
                if k is not False][0]
    elif tscore == dscore:
        # same score or both False
        return False
    elif tscore > dscore:
        return tfeature
    elif tscore < dscore:
        return dfeature
    else:
        # in case uncaught edgecase occurs
        print('WARNING, target score {} and decoy score {} could not be '
              'compared'.format(tscore, dscore))
        return False

def generate_top_ms1_peptides(peptides, protcol):
    """Fed with a peptides generator, this returns the 3 PSMs with
    the highest precursor intensities (or areas, or whatever is
    given in the HEADER_PRECURSOR_QUANT"""
    top_ms1_peps = {}
    for peptide in peptides:
        protacc = peptide[protcol]
        precursor_amount = peptide[peptabledata.HEADER_AREA]
        if ';' in protacc or precursor_amount == 'NA':
            continue
        precursor_amount = float(precursor_amount)
        pepseq = peptide[peptabledata.HEADER_PEPTIDE]
        try:
            peptide_area = top_ms1_peps[protacc][pepseq]
        except KeyError:
            try:
                top_ms1_peps[protacc][pepseq] = precursor_amount
            except KeyError:
                top_ms1_peps[protacc] = {pepseq: precursor_amount}
        else:
            if precursor_amount > peptide_area:
                top_ms1_peps[protacc][pepseq] = precursor_amount
    return top_ms1_peps

def calculate_protein_precursor_quant(top_ms1_peps, prot_acc):
    try:
        amounts = top_ms1_peps[prot_acc].values()
    except KeyError:
        return 'NA'
    else:
        amounts = sorted([x for x in amounts if x > 0], reverse=True)[:3]
        return sum(amounts) / len(amounts)

def add_ms1_quant_from_top3_mzidtsv(features, peptides, outputaccfield, featcol):
    """Collects PSMs with the highest precursor quant values,
    adds sum of the top 3 of these to a protein table"""
    top_ms1_peps = generate_top_ms1_peptides(peptides, featcol)
    for feat in features:
        acc = feat[outputaccfield]
        prec_area = calculate_protein_precursor_quant(top_ms1_peps, acc)
        outfeat = {k: v for k, v in feat.items()}
        outfeat[prottabledata.HEADER_AREA] = str(prec_area)
#        outprotein[headerfields['precursorquant'][
#            prottabledata.HEADER_AREA][None]] = str(prec_area)
        yield outfeat
<|endoftext|>