diff --git a/.github/workflows/.pylintrc b/.github/workflows/.pylintrc new file mode 100644 index 00000000..1b892797 --- /dev/null +++ b/.github/workflows/.pylintrc @@ -0,0 +1,7 @@ +[FORMAT] +max-line-length=150 +max-args=15 +max-locals=40 + +[MESSAGES CONTROL] +disable = E0401, W0719 diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml new file mode 100644 index 00000000..5b7e7e11 --- /dev/null +++ b/.github/workflows/pylint.yml @@ -0,0 +1,26 @@ +name: Pylint + +on: + push: + branches: + - '*' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10"] + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pylint + - name: Analysing the code with pylint + run: | + pylint --fail-under=9 $(git ls-files '*.py') --rcfile=/home/runner/work/CRISPRessoReports/CRISPRessoReports/.github/workflows/.pylintrc diff --git a/CRISPRessoReport.py b/CRISPRessoReport.py index 58cf42ac..e6616ae1 100644 --- a/CRISPRessoReport.py +++ b/CRISPRessoReport.py @@ -79,7 +79,7 @@ def add_fig_if_exists(fig_name, fig_root, fig_title, fig_caption, fig_data, if os.path.exists(os.path.join(crispresso_folder, data_file)): amplicon_figures['datas'][fig_name].append((data_caption, data_file)) if os.path.exists(htmlfullpath): - with open(htmlfullpath) as html: + with open(htmlfullpath, encoding="utf-8") as html: html_string = "
" html_string += html.read() html_string += "
" @@ -87,6 +87,9 @@ def add_fig_if_exists(fig_name, fig_root, fig_title, fig_caption, fig_data, def assemble_figs(run_data, crispresso_folder): + """ + Helper function create the data structre for the figures + """ figures = {'names': {}, 'locs': {}, 'titles': {}, 'captions': {}, 'datas': {}, 'htmls': {}, 'sgRNA_based_names': {}} global_fig_names = [] @@ -138,7 +141,7 @@ def assemble_figs(run_data, crispresso_folder): return data -def make_report(run_data, crispresso_report_file, crispresso_folder, _ROOT, web_version=False): +def make_report(run_data, crispresso_report_file, crispresso_folder, _ROOT): # dicts for each amplicon fig_names[amp_name] = [list of fig names] # fig_locs[amp_name][fig_name] = figure location # print('crispresso_report file: ' + crispresso_report_file + ' crispresso_folder : ' + crispresso_folder + ' root: ' + _ROOT) @@ -169,7 +172,7 @@ def make_report(run_data, crispresso_report_file, crispresso_folder, _ROOT, web_ # shutil.copy2(os.path.join(_ROOT,'templates','CRISPResso_justcup.png'),dest_dir) # shutil.copy2(os.path.join(_ROOT,'templates','favicon.ico'),dest_dir) - with open(crispresso_report_file, 'w') as outfile: + with open(crispresso_report_file, 'w', encoding="utf-8") as outfile: outfile.write(render_template( 'report.html', j2_env, report_data=report_data, )) @@ -244,12 +247,12 @@ def make_batch_report_from_folder(crispressoBatch_report_file, crispresso2_info, allele_modification_heatmap_plot['htmls'] = {} for heatmap_plot_name, heatmap_plot_path in allele_modification_heatmap_plot['paths'].items(): - with open(heatmap_plot_path) as fh: + with open(heatmap_plot_path, encoding="utf-8") as fh: allele_modification_heatmap_plot['htmls'][heatmap_plot_name] = fh.read() allele_modification_line_plot['htmls'] = {} for line_plot_name, line_plot_path in allele_modification_line_plot['paths'].items(): - with open(line_plot_path) as fh: + with open(line_plot_path, encoding="utf-8") as fh: allele_modification_line_plot['htmls'][line_plot_name] = fh.read() #find path between the report and the data (if the report is in another directory vs in the same directory as the data) @@ -257,7 +260,7 @@ def make_batch_report_from_folder(crispressoBatch_report_file, crispresso2_info, if crispresso_data_path == ".": crispresso_data_path = "" else: - crispresso_data_path += "/"; + crispresso_data_path += "/" sub_html_files = {} run_names = [] @@ -267,7 +270,7 @@ def make_batch_report_from_folder(crispressoBatch_report_file, crispresso2_info, crispresso_folder = os.path.join(batch_folder, sub_folder) run_data = CRISPRessoShared.load_crispresso_info(crispresso_folder) if 'running_info' not in run_data: - raise Exception('CRISPResso run %s has no report. Cannot add to batch report.' % sub_folder) + raise Exception(f'CRISPResso run {sub_folder} has no report. Cannot add to batch report.') this_sub_html_file = sub_folder + ".html" if run_data['running_info']['args'].place_report_in_output_folder: @@ -278,7 +281,7 @@ def make_batch_report_from_folder(crispressoBatch_report_file, crispresso2_info, output_title = 'CRISPResso Batch Output' if crispresso2_info['running_info']['args'].name != '': - output_title += '
{0}'.format(crispresso2_info['running_info']['args'].name) + output_title += f"
{crispresso2_info['running_info']['args'].name}" make_multi_report( run_names, @@ -307,7 +310,7 @@ def make_pooled_report_from_folder(crispresso_report_file, crispresso2_info, fol names_arr = crispresso2_info['results']['good_region_names'] output_title = 'CRISPResso Pooled Output' if crispresso2_info['running_info']['args'].name != '': - output_title += '
{0}'.format(crispresso2_info['running_info']['args'].name) + output_title += f"
{crispresso2_info['running_info']['args'].name}" make_multi_report_from_folder(crispresso2_info, names_arr, output_title, crispresso_report_file, folder, _ROOT, 'pooled') @@ -315,7 +318,7 @@ def make_compare_report_from_folder(crispresso_report_file, crispresso2_info, fo names_arr = [] output_title = 'CRISPResso Compare Output' if crispresso2_info['running_info']['args'].name != '': - output_title += '
{0}'.format(crispresso2_info['running_info']['args'].name) + output_title += "
{crispresso2_info['running_info']['args'].name}" make_multi_report_from_folder(crispresso2_info, names_arr, output_title, crispresso_report_file, folder, _ROOT, 'compare') @@ -324,7 +327,7 @@ def make_meta_report_from_folder(crispresso_report_file, crispresso2_info, folde input_names = crispresso2_info['meta_input_names'] output_title = 'CRISPresso Meta Output' if crispresso2_info['running_info']['args'].name != '': - output_title += '
{0}'.format(crispresso2_info['running_info']['args'].name) + output_title += "
{crispresso2_info['running_info']['args'].name}" make_multi_report_from_folder(crispresso2_info, names_arr, output_title, crispresso_report_file, folder, _ROOT, 'meta', display_names=input_names) @@ -333,7 +336,7 @@ def make_wgs_report_from_folder(crispresso_report_file, crispresso2_info, folder names_arr = crispresso2_info['results']['good_region_names'] output_title = 'CRISPResso WGS Output' if crispresso2_info['running_info']['args'].name != '': - output_title += '
{0}'.format(crispresso2_info['running_info']['args'].name) + output_title += "
{crispresso2_info['running_info']['args'].name}" make_multi_report_from_folder(crispresso2_info, names_arr, output_title, crispresso_report_file, folder, _ROOT, 'wgs') @@ -349,10 +352,11 @@ def make_multi_report_from_folder(crispresso2_info, names_arr, report_name, cris crispresso_report_file (string): path to write report to folder (string): folder containing crispresso runs _ROOT (string): location of crispresso assets (images, templates, etc) - display_names (dict): report_name->display_name; Titles to be shown for crispresso runs (if different from names_arr, e.g. if display_names have spaces or bad chars, they won't be the same as names_arr) + display_names (dict): report_name->display_name; Titles to be shown for crispresso runs + (if different from names_arr, e.g. if display_names have spaces or bad chars, they won't be the same as names_arr) Returns: - Nothin + Nothing """ summary_plot_names = [] @@ -378,11 +382,11 @@ def make_multi_report_from_folder(crispresso2_info, names_arr, report_name, cris if display_names is not None: display_name = display_names[name] - folder_name = 'CRISPResso_on_%s' % name + folder_name = f'CRISPResso_on_{name}' sub_folder = os.path.join(folder, folder_name) run_data = CRISPRessoShared.load_crispresso_info(sub_folder) if 'running_info' not in run_data: - raise Exception('CRISPResso run %s has no report. Cannot add to report.' % sub_folder) + raise Exception(f'CRISPResso run {sub_folder} has no report. Cannot add to report.') run_names.append(display_name) @@ -428,19 +432,14 @@ def make_multi_report( _ROOT, report_name, crispresso_tool, - window_nuc_pct_quilts=[], - nuc_pct_quilts=[], - window_nuc_conv_plots=[], - nuc_conv_plots=[], - summary_plots={ - 'names': [], - 'titles': [], - 'labels': [], - 'datas': [], - }, - compact_plots_to_show={}, - allele_modification_heatmap_plot={}, - allele_modification_line_plot={}, + window_nuc_pct_quilts=None, + nuc_pct_quilts=None, + window_nuc_conv_plots=None, + nuc_conv_plots=None, + summary_plots=None, + compact_plots_to_show=None, + allele_modification_heatmap_plot=None, + allele_modification_line_plot=None, ): """ Makes an HTML report for a run containing multiple crispresso runs @@ -494,6 +493,10 @@ def fill_default(dictionary, key, default_type=list): else: crispresso_data_path += "/" + if allele_modification_heatmap_plot is None: + allele_modification_heatmap_plot = {} + if allele_modification_line_plot is None: + allele_modification_line_plot = {} dictionaries = [ allele_modification_heatmap_plot, allele_modification_line_plot, ] @@ -511,18 +514,24 @@ def fill_default(dictionary, key, default_type=list): key, default_type, ) - web = False - if not web: - for html in sub_html_files: - sub_html_files[html] = crispresso_data_path + sub_html_files[html] - with open(crispresso_multi_report_file, 'w') as outfile: + if summary_plots is None: + summary_plots={ + 'names': [], + 'titles': [], + 'labels': [], + 'datas': [], + } + + for html in sub_html_files: + sub_html_files[html] = crispresso_data_path + sub_html_files[html] + with open(crispresso_multi_report_file, 'w', encoding="utf-8") as outfile: outfile.write(render_template( template, j2_env, - window_nuc_pct_quilts=window_nuc_pct_quilts, - nuc_pct_quilts=nuc_pct_quilts, - window_nuc_conv_plots=window_nuc_conv_plots, - nuc_conv_plots=nuc_conv_plots, + window_nuc_pct_quilts=[] if window_nuc_pct_quilts is None else window_nuc_pct_quilts, + nuc_pct_quilts=[] if nuc_pct_quilts is None else nuc_pct_quilts, + window_nuc_conv_plots=[] if window_nuc_conv_plots is None else window_nuc_conv_plots, + nuc_conv_plots=[] if nuc_conv_plots is None else nuc_conv_plots, crispresso_data_path=crispresso_data_path, report_data={ 'names': summary_plots['names'], @@ -535,7 +544,7 @@ def fill_default(dictionary, key, default_type=list): run_names=run_names, sub_html_files=sub_html_files, report_name=report_name, - compact_plots_to_show=compact_plots_to_show, + compact_plots_to_show=[] if compact_plots_to_show is None else compact_plots_to_show, allele_modification_heatmap_plot_names=allele_modification_heatmap_plot['names'], allele_modification_heatmap_plot_htmls=allele_modification_heatmap_plot['htmls'], allele_modification_heatmap_plot_titles=allele_modification_heatmap_plot['titles'], @@ -557,7 +566,7 @@ def make_aggregate_report( _ROOT, folder_arr, crispresso_html_reports, - compact_plots_to_show={}, + compact_plots_to_show=None, display_names=None, ): """ @@ -572,10 +581,11 @@ def make_aggregate_report( folder_arr (arr of strings): paths to the aggregated crispresso folders crispresso_html_reports (dict): folder->html_path; Paths to the aggregated crispresso run html reports compact_plots_to_show (dict): name=>{'href': path to target(report) when user clicks on image, 'img': path to png image to show} - display_names (dict): folder->display_name; Titles to be shown for crispresso runs (if different from names_arr, e.g. if display_names have spaces or bad chars, they won't be the same as names_arr) + display_names (dict): folder->display_name; Titles to be shown for crispresso runs + (if different from names_arr, e.g. if display_names have spaces or bad chars, they won't be the same as names_arr) Returns: - Nothin + Nothing """ summary_plots = {} if 'summary_plot_names' in crispresso2_info['results']['general_plots']: @@ -649,7 +659,7 @@ def make_aggregate_report( run_names = [] sub_html_files = {} - for idx, folder in enumerate(folder_arr): + for folder in folder_arr: display_name = folder if display_names is not None: display_name = display_names[folder] @@ -657,7 +667,8 @@ def make_aggregate_report( run_names.append(display_name) sub_html_file = os.path.relpath(crispresso_html_reports[folder], crispresso_report_folder) sub_html_files[display_name] = sub_html_file - + if compact_plots_to_show is None: + compact_plots_to_show = {} for compact_plot in compact_plots_to_show: old_href = compact_plots_to_show[compact_plot]['href'] compact_plots_to_show[compact_plot]['href'] = os.path.relpath(old_href, crispresso_report_folder) @@ -666,12 +677,12 @@ def make_aggregate_report( allele_modification_heatmap_plot['htmls'] = {} for heatmap_plot_name, heatmap_plot_path in allele_modification_heatmap_plot['paths'].items(): - with open(heatmap_plot_path) as fh: + with open(heatmap_plot_path, encoding="utf-8") as fh: allele_modification_heatmap_plot['htmls'][heatmap_plot_name] = fh.read() allele_modification_line_plot['htmls'] = {} for line_plot_name, line_plot_path in allele_modification_line_plot['paths'].items(): - with open(line_plot_path) as fh: + with open(line_plot_path, encoding="utf-8") as fh: allele_modification_line_plot['htmls'][line_plot_name] = fh.read() make_multi_report( diff --git a/README.md b/README.md index 96db5a06..23b7b4a3 100644 --- a/README.md +++ b/README.md @@ -1 +1,50 @@ # CRISPRessoReports +Git Subtree Standard Operating Procedure (SOP) + +Created: February 3, 2023 + +Introduction +In a few of our repositories we use git subtrees to share common files shared across multiple repositories. The goal of using subtrees is to reduce duplicated code. + +Process +If you have a repository where you would like to add a subtree, run the following command from the parent repo (wherein you would like the external child repo to be stored). + +git subtree add --prefix {path to where subtree will be stored} --squash {url of child repo} {branch of child repo} + +Some notes about the above command: +- You can’t add a subtree to an empty repo. +- If you don’t specify the --squash parameter then all of the commits from the child repo will be brought into the parent repo history. +- When you do specify the --squash parameter then there will be two commits in the parent repo history. +- If you have the url of the child repo added as a remote, you can use that instead. +- You should now see the files in the child repo at the path you specified in the parent repo. + +If you edit the files in the child repo (from within the parent repo), you can push the changes you have made up to the child repo with this command. + +git subtree push --prefix {path to where subtree is stored} --rejoin {url of child repo} {branch of child repo} + +Some notes about the above command: +- You should separate your commits such that there are not files staged in both child and parent. +- If there are commits in the child repo that you need in the parent repo, you can use this command. + +git subtree pull --prefix {path to where subtree is stored} --squash {url of child repo} {branch of child repo} + +Some notes about the above command: +- A merge conflict can happen here, so be warned! + +If you are doing this regularly, the commands above can be cumbersome. You can create an alias in git to make these commands easier! Edit the .git/config file in your repo and add the following lines to it. + +[alias] + +The acronym stands for "CrispressoReports Add" + +cra = "!f() { git subtree add --prefix CRISPRessoWEB/CRISPRessoReports https://github.com/edilytics/CRISPRessoReports.git $1 --squash; }; f" + +The acronym stands for "CrispressoReports Update" + +cru = "!f() { git subtree pull --prefix CRISPRessoWEB/CRISPRessoReports https://github.com/edilytics/CRISPRessoReports.git $1 --squash; }; f" + +The acronym stands for "CrispressoReports Push" + +crp = "!f() { git subtree push --prefix CRISPRessoWEB/CRISPRessoReports https://github.com/edilytics/CRISPRessoReports.git $1; }; f" + + diff --git a/templates/batchReport.html b/templates/batchReport.html index d02c6269..58a41e97 100644 --- a/templates/batchReport.html +++ b/templates/batchReport.html @@ -148,7 +148,7 @@
{{report_data['titles'][plot_name]}}
{# data bit for web version: #} {# -

Data: {{data_label}}

+

Data: {{data_label}}

#} {# jumbotron_content #} {# jumbrotron #} diff --git a/templates/layout.html b/templates/layout.html index dba520de..2abb7e04 100644 --- a/templates/layout.html +++ b/templates/layout.html @@ -1,237 +1,237 @@ - - CRISPResso2 - - - - - - - - - - - - - - - - - - - - - - - {% if current_user is defined %} - - {% else %} - - {% endif %} - - - - - {% block head %}{% endblock %} - - - - - {% if current_user is defined %} -
- {{ self.help_block() }} -
- - {% with messages = get_flashed_messages(with_categories=true) %} - {% if messages %} - {% for category, message in messages %} - {% if category == "error" %} - - {% else %} - - {% endif %} - {% endfor %} - {% endif %} - {% endwith %} - {% endif %} - - {# if default user (normal crispresso mode) #} - {% if current_user == null or not current_user.is_anonymous and current_user.username == "DEFAULTUSER" %} - -
-
-
- {% if current_user is defined %} -
- {% else %} -
- {% endif %} -
-
-

CRISPResso2

-

Analysis of genome editing outcomes from deep sequencing data

-
-
-
-
- - - {% else %} - {# if doing user sessions #} - +
+ {% endif %} + +
+
+
+
+ {% block help_block %} {% endblock %} +
+
+
+
+ + {% block content %}{% endblock %} + + {% if current_user is defined and (not current_user.is_anonymous and current_user.username == "DEFAULTUSER") %} +
+
+ -
-
- {% else %} -
-
- -
-
- {% endif %} + Clement K, Rees H, Canver MC, Gehrke JM, Farouni R, Hsu JY, Cole MA, Liu DR, Joung JK, Bauer DE, Pinello L.
+ CRISPResso2 provides accurate and rapid genome editing sequence analysis.
+ Nat Biotechnol. 2019 Mar; 37(3):224-226. doi: 10.1038/s41587-019-0032-3. PubMed PMID: 30809026. +

+

© Copyright Kendell Clement and Luca Pinello

+

+ + + +

+ + +
+
+ {% else %} +
+
+ +
+
+ {% endif %}
- - - - - {% block foot %}{% endblock %} - + + + + + {% block foot %}{% endblock %} + diff --git a/templates/report.html b/templates/report.html index fb755d18..775aa0d1 100644 --- a/templates/report.html +++ b/templates/report.html @@ -24,27 +24,53 @@ background-color:lightsteelblue; } -@media print { - .tab-content > .tab-pane { - display: block !important; - opacity: 1 !important; - visibility: visible !important; - margin-bottom: 2em !important; - page-break-inside: avoid; - } - .nav-tabs { - display:none !important; - visibility:hidden !important; - } - .tab-content.amp-body { - background-color:transparent !important; - border:None !important; - } -} @media only screen and (max-width: 600px) { .jumbotron img { width:100% } + .print-only{ + display: none !important; + } + .screen-only{ + display: block; + } +} + +@media print { + .tab-content > .tab-pane { + display: block !important; + opacity: 1 !important; + visibility: visible !important; + margin-bottom: .5em !important; + } + .nav-tabs { + display:none !important; + visibility:hidden !important; + } + .tab-content.amp-body { + background-color:transparent !important; + border:None !important; + } + .col-sm-10 { + margin-left: -15%; + width: 130% !important; + } + .breakinpage { + clear: both; + page-break-before: always !important; + display: block; + } + .print-only, .print-only * + { + display: block; + } + .screen-only, .screen-only * + { + display: none !important; + } + div { + border: none !important; + } } @@ -75,12 +101,13 @@
CRISPResso2 run information
{{ render_partial('shared/partials/fig_reports.html', report_data=report_data, fig_name='plot_1a')}}
+ {{ render_partial('shared/partials/log_params.html', report_data=report_data) }}
-
+
Allele assignments