diff --git a/.gitignore b/.gitignore index 02e51a55..05c337f0 100644 --- a/.gitignore +++ b/.gitignore @@ -129,7 +129,4 @@ dmypy.json .pyre/ #vscode -.vscode/ - -#Excel files -*.xlsx \ No newline at end of file +.vscode/ \ No newline at end of file diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 1b9ec2c2..00000000 --- a/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -# This docker file coppies the src folder and installs the dependencies -# we use python:3.12 as the base image -# we also mount the output folder to the WORKDIR/output folder - -FROM python:3.12 - -# Set the working directory -WORKDIR /app - -# Copy the current directory contents into the container at /app -COPY ./src /app - -# Install any needed packages specified in requirements.txt -RUN pip install --trusted-host pypi.python.org -r requirements.txt - -# Create a folder to store the output -RUN mkdir output - -# Expose the port the app runs on -EXPOSE 4269 - -# Run main.py when the container launches -CMD ["python", "main.py"] diff --git a/README.md b/README.md index 3cecbfab..a42bab1a 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,3 @@ -# Xperimental Data Connector +# Xperimental-data-convertor - -XDC logo - - -The Xperimental Data Connector (XDC) is a Python package that links excel2sbol and excel2flapjack, interlink the resulting data, and upload it to synbiohub and flapjack. - -This branch is only used to generate an Excel sheet for experiments. It does this by taking the cartesian product of the options selected to generate an experiment that covers all possible variations of the experiment. \ No newline at end of file +This is a utility to link excel2sbol and excel to flapjack converter, interlink the resulting data, and upload it to synbiohub and flapjack. diff --git a/docker-compose.yaml b/docker-compose.yaml deleted file mode 100644 index e93f3d56..00000000 --- a/docker-compose.yaml +++ /dev/null @@ -1,9 +0,0 @@ -services: - main: - build: - context: . - dockerfile: Dockerfile - volumes: - - ./output:/app/output - ports: - - "4269:4269" \ No newline at end of file diff --git a/images/Dependency_structure.pptx b/images/Dependency_structure.pptx new file mode 100644 index 00000000..a4718b97 Binary files /dev/null and b/images/Dependency_structure.pptx differ diff --git a/images/Excel_Sheet_Parts.png b/images/Excel_Sheet_Parts.png new file mode 100644 index 00000000..e9b663cb Binary files /dev/null and b/images/Excel_Sheet_Parts.png differ diff --git a/images/dependency_structure_20210611.png b/images/dependency_structure_20210611.png new file mode 100644 index 00000000..8797f9b8 Binary files /dev/null and b/images/dependency_structure_20210611.png differ diff --git a/images/excel2sbol_spreadsheet.PNG b/images/excel2sbol_spreadsheet.PNG new file mode 100644 index 00000000..580f8280 Binary files /dev/null and b/images/excel2sbol_spreadsheet.PNG differ diff --git a/images/excel2sbol_synbiohub.PNG b/images/excel2sbol_synbiohub.PNG new file mode 100644 index 00000000..27672793 Binary files /dev/null and b/images/excel2sbol_synbiohub.PNG differ diff --git a/images/excel2sbol_xml.PNG b/images/excel2sbol_xml.PNG new file mode 100644 index 00000000..92a4124f Binary files /dev/null and b/images/excel2sbol_xml.PNG differ diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..e568a20f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +excel2flapjack~=1.0.2 +excel2sbol~=1.0.24 diff --git a/run-native.sh b/run-native.sh deleted file mode 100755 index d187319c..00000000 --- a/run-native.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash - -if [[ "$@" == *"--clean"* ]]; then - rm -r venv -fi - -if [ ! -d "venv" ]; then - python3 -m venv venv - . venv/bin/activate - pip install -r src/requirements.txt -else - . venv/bin/activate -fi - -if [[ "$@" == *"--debug"* ]]; then - python3 src/main.py --debug -else - python src/main.py -fi diff --git a/src/favicon.ico b/src/favicon.ico deleted file mode 100644 index d9e15df1..00000000 Binary files a/src/favicon.ico and /dev/null differ diff --git a/src/gensheet.py b/src/gensheet.py deleted file mode 100644 index e605e3e3..00000000 --- a/src/gensheet.py +++ /dev/null @@ -1,106 +0,0 @@ -from datetime import datetime -import itertools -from openpyxl import Workbook -from openpyxl.styles import Font - -# Below is excel sheet generation code -# it takes an argument that looks like [{'lb'}, {'top10', 'dh5a'}, {'repressilator'}, {'atc'}] -def excel_sheet(lists, replicates): - product = list(itertools.product(*lists)) - wb = Workbook() - sheet1 = wb.active - sheet1.title = "Sample Design" - titles = [ - "Sample Design ID", - "Media ID", - "Strain ID", - "Vector ID", - "Supplement ID", - ] - - # Populate the first row with the titles - for col_num, title in enumerate(titles, start=1): - cell = sheet1.cell(row=1, column=col_num, value=title) - cell.font = Font(bold=True) - - global sample_design_id - sample_design_id = 1 - - # Populating the excel sheet with generated data - for row_num, item in enumerate(product, start=2): - # Sample ID counter for first row - sheet1.cell(row=row_num, column=1, value=f"SampleDesign{sample_design_id}") - sample_design_id += 1 - - # Continue to populate the rest of the row - for col_num, value in enumerate(item, start=2): - sheet1.cell(row=row_num, column=col_num, value=value) - - plate_96_wells_coordinates = [ - (row, col) for row in range(1, 9) for col in range(1, 13) - ] - - def generate_sample_names(num_designs): - global sample_names - sample_names = [] - for i in range(0, sample_design_id - 1): - sample_names.append(f"SampleDesign{i+1}") - return sample_names - - num_designs = 4 - generate_sample_names(num_designs) - - # This creates/formats sheet 2 with the samples and their positions in a well plate - sheet2 = wb.create_sheet(title="Sample") - - headers = ["Sample ID", "Row", "Column", "Well ID", "Sample Design ID"] - - for col_num, header in enumerate(headers, start=1): - sheet2.cell(row=1, column=col_num, value=header) - - # Initializes the well count and sample ID count - well_count = 0 - sample_id = 1 - - for sd in sample_names: - for r in range(replicates): - row_data = [ - f"Sample{sample_id}", - plate_96_wells_coordinates[well_count][0], - plate_96_wells_coordinates[well_count][1], - f"Assay{+1}", - sd, - ] - sheet2.append(row_data) - well_count += 1 - sample_id += 1 - - # Auto fit column width code - for col in sheet1.columns: - max_length = 0 - column = col[0].column_letter - for cell in col: - try: - if len(str(cell.value)) > max_length: - max_length = len(str(cell.value)) - except: - pass - adjusted_width = max_length + 2 - sheet1.column_dimensions[column].width = adjusted_width - - for col in sheet2.columns: - max_length = 0 - column = col[0].column_letter - for cell in col: - try: - if len(str(cell.value)) > max_length: - max_length = len(str(cell.value)) - except: - pass - adjusted_width = max_length + 2 - sheet2.column_dimensions[column].width = adjusted_width - - timestamp = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") - filename = f"output/SampleDesign_{timestamp}.xlsx" - - wb.save(filename) diff --git a/src/main.py b/src/main.py deleted file mode 100644 index 8dc8ac35..00000000 --- a/src/main.py +++ /dev/null @@ -1,66 +0,0 @@ -import argparse -from flask import Flask, request, render_template, send_file -import webbrowser -from gensheet import excel_sheet - -app = Flask(__name__) - -@app.route("/") -@app.route('/index.html') -def index(): - return send_file('templates/index.html', mimetype='text/html') - -@app.route('/options.json') -def options(): - return send_file('options.json', mimetype='application/json') - -@app.route('/favicon.ico') -def favicon(): - return send_file('favicon.ico', mimetype='image/vnd.microsoft.icon') - -# This takes an html form post and prints it to the console returning success to the browser -@app.route('/submit', methods=['POST']) -def submit(): - # get the lists from the form - lists = [ - request.form.getlist('media'), - request.form.getlist('strain'), - request.form.getlist('supplement'), - request.form.getlist('vector') - ] - - #check that all lists have at least one item - for l in lists: - if len(l) == 0: - return render_template('response.html', - message="Error, at least one item must be selected from each list, please go back.", - color="red" - ) - - # get the number of replicates from the form - replicates = int(request.form["replicates"]) - - # generate the excel sheet - try: - excel_sheet(lists, replicates) - # Below is error handling code - except Exception as e: - return render_template('response.html', - message=f"An error occurred during generation: {str(e)}\n\nPlease select a lower number of replicates and try again.", - color="red" - ) - - # return success to the browser - return render_template('response.html', - message="Success", - color="green" - ) - -port = 4269 -# webbrowser.open('http://localhost:'+str(port)) -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Run Flask app with optional debug mode.') - parser.add_argument('--debug', action='store_true', help='Run the Flask app in debug mode') - args = parser.parse_args() - - app.run(debug=args.debug, port=port, host='0.0.0.0') diff --git a/src/options.json b/src/options.json deleted file mode 100644 index 613e033d..00000000 --- a/src/options.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "media": ["lb", "m9"], - "strain": ["top10", "dh5a"], - "supplement": ["atc", "iptg"], - "vector": ["repressilator", "toggleswitch"] -} \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt deleted file mode 100644 index 30c2d9ed..00000000 --- a/src/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -flask==3.0.3 -openpyxl==3.1.5 \ No newline at end of file diff --git a/src/templates/index.html b/src/templates/index.html deleted file mode 100644 index 5cf5fa6a..00000000 --- a/src/templates/index.html +++ /dev/null @@ -1,131 +0,0 @@ - - - - - - LabGen UI - - - -
-

Welcome to LabGen

-
-
- - - - - - - diff --git a/src/templates/response.html b/src/templates/response.html deleted file mode 100644 index d2c304d5..00000000 --- a/src/templates/response.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - Success - - -

Server Says:

{{message}}

- Go Back - - \ No newline at end of file diff --git a/temp.py b/temp.py new file mode 100644 index 00000000..5885c32f --- /dev/null +++ b/temp.py @@ -0,0 +1,18 @@ +from sre_constants import SUCCESS +import xperimental_data_conv.main as xdc +import os + +fj_user = "" +fj_pass = "" +sbh_pass = '' +sbh_user = '' + + +direct = os.path.split(__file__)[0] +file_path_in = os.path.join(direct, 'xperimental-data-conv','tests','test_files', 'flapjack_excel_converter_v030.xlsx') +sbh_overwrite = '1' +sbh_collec = 'Flapjack' + +sbol_collec_url = xdc.experimental_data_uploader(file_path_in, fj_user, fj_pass, + sbh_user, sbh_pass, sbh_collec, sbh_overwrite=True, + fj_overwrite=True) diff --git a/xperimental-data-conv/MANIFEST.in b/xperimental-data-conv/MANIFEST.in new file mode 100644 index 00000000..46121e8b --- /dev/null +++ b/xperimental-data-conv/MANIFEST.in @@ -0,0 +1 @@ +recursive-include excel2sbol *.txt diff --git a/xperimental-data-conv/README.md b/xperimental-data-conv/README.md new file mode 100644 index 00000000..76ed6b60 --- /dev/null +++ b/xperimental-data-conv/README.md @@ -0,0 +1 @@ +This library is a collection of functions used to convert an excel template into SBOL libraries and compositions. \ No newline at end of file diff --git a/xperimental-data-conv/setup.py b/xperimental-data-conv/setup.py new file mode 100644 index 00000000..92fa1a3c --- /dev/null +++ b/xperimental-data-conv/setup.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from setuptools import find_packages, setup + +setup(name='xperimental-data-conv', + version='1.0.0-alpha-9', + url='https://github.com/SynBioDex/Experimental-Data-Convertor', + license='BSD 3-clause', + maintainer='Jet Mante', + maintainer_email='jet.mante@colorado.edu', + include_package_data=True, + description='convert excel resources into sbol and flapjack and upload them', + packages=find_packages(include=['xperimental_data_conv']), + long_description=open('README.md').read(), + install_requires=['excel2flapjack~=1.0.2', + 'excel2sbol~=1.0.24'], + zip_safe=False) diff --git a/xperimental-data-conv/tests/test_files/flapjack_excel_converter_v030.xlsx b/xperimental-data-conv/tests/test_files/flapjack_excel_converter_v030.xlsx new file mode 100644 index 00000000..84a4be18 Binary files /dev/null and b/xperimental-data-conv/tests/test_files/flapjack_excel_converter_v030.xlsx differ diff --git a/xperimental-data-conv/xperimental_data_conv/__init__.py b/xperimental-data-conv/xperimental_data_conv/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/xperimental-data-conv/xperimental_data_conv/main.py b/xperimental-data-conv/xperimental_data_conv/main.py new file mode 100644 index 00000000..4d2a75f6 --- /dev/null +++ b/xperimental-data-conv/xperimental_data_conv/main.py @@ -0,0 +1,117 @@ +import excel2flapjack.main as e2f +import excel2sbol.converter as conv +import sbol2 +import tempfile +import requests +import os + + +def experimental_data_uploader(file_path_in, fj_user, fj_pass, sbh_user, + sbh_pass, sbh_collec, sbh_overwrite=False, + fj_overwrite=False): + + # create temporary directory to write intermediate files to + temp_dir = tempfile.TemporaryDirectory() + file_path_out = os.path.join(temp_dir.name, 'test.xml') + file_path_out2 = os.path.join(temp_dir.name, 'test1.xml') + + # convert the excel file to SBOL + # use excel2sbol - could ask for an update that just gives the doc back rather than the file + homespace = 'http://www.examples.org/' + conv.converter(file_path_in, file_path_out, homespace=homespace) + + # SBH Login + response = requests.post( + 'https://synbiohub.org/login', + headers={'Accept': 'text/plain'}, + data={ + 'email': sbh_user, + 'password' : sbh_pass, + } + ) + x_token = response.text + + # Pull graph uri from synbiohub + response = requests.get( + 'https://synbiohub.org/profile', + headers={ + 'Accept': 'text/plain', + 'X-authorization': x_token + } + ) + sbol_graph_uri = response.json()['graphUri'] + sbol_collec_url = f'{sbol_graph_uri}/{sbh_collec}/' + + # Parse sbol to create hashmap of flapjack id to sbol uri + doc = sbol2.Document() + doc.read(file_path_out) + + sbol_hash_map = {} + for tl in doc: + if 'https://flapjack.rudge-lab.org/ID' in tl.properties: + sbol_uri = tl.properties['http://sbols.org/v2#persistentIdentity'][0] + sbol_uri = sbol_uri.replace(homespace, sbol_collec_url) + sbol_uri = f'{sbol_uri}/1' + + sbol_name = str(tl.properties['http://sbols.org/v2#displayId'][0]) + sbol_hash_map[sbol_name] = sbol_uri + + + # upload the excel file to flapjack and get hash map back + fj_url = "flapjack.rudge-lab.org:8000" + # hash_map = e2f.flapjack_upload(fj_url, fj_user, fj_pass, file_path_in) + hash_map = e2f.flapjack_upload(fj_url, fj_user, fj_pass, file_path_in, + sbol_hash_map=sbol_hash_map, + add_sbol_uris=True, + flapjack_override=fj_overwrite, + print_progress=True) + # print(hash_map) + + # Add flapjack annotations to the SBOL + doc = sbol2.Document() + doc.read(file_path_out) + for tl in doc: + id = str(tl).split('/')[-2] + if id in hash_map: + setattr(tl, 'flapjack_ID', + sbol2.URIProperty(tl, + 'https://flapjack.rudge-lab.org/ID', + '0', '*', [], initial_value=f'http://wwww.flapjack.com/{hash_map[id]}')) + doc.write(file_path_out2) + + if sbh_overwrite: + sbh_overwrite = '1' + else: + sbh_overwrite = '0' + # SBH file upload + response = requests.post( + 'https://synbiohub.org/submit', + headers={ + 'Accept': 'text/plain', + 'X-authorization': x_token + }, + files={ + 'files': open(file_path_out2,'rb'), + }, + data={ + 'id': sbh_collec, + 'version' : '1', + 'name' : sbh_collec, + 'description' : 'Description stuff', + 'overwrite_merge' : sbh_overwrite + }, + + ) + + if response.text == "Submission id and version already in use": + print('not submitted') + raise AttributeError(f'The collection ({sbh_collec}) could not be submitted to synbiohub as the collection already exists and overite is not on. Note it was submitted to flapjack') + # if response.text == "Successfully uploaded": + # success = True + + return(sbol_collec_url) + + + + +