From fce6cba38f51815cebb6e1f6dfe3341efe5a0bf1 Mon Sep 17 00:00:00 2001 From: mauricio Date: Thu, 19 Oct 2023 17:01:03 -0300 Subject: [PATCH 01/11] examples in documentation --- README.md | 233 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a24200a..bd4769a 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,46 @@ Receives a miRNA ID (mirbase MIMAT ID or previous ID) and returns a paginated ve - `pubmeds`: array of pubmed for the miRNA-gene interaction (according to mirTaRBase). - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) + - Example: + - URL: http://localhost:8000/mirna-interactions?mirna=hsa-miR-891a-5p&search=EGFR + - Response: + ```json + { + "count":1, + "next":null, + "previous":null, + "results":[ + { + "id":629118277, + "mirna":"hsa-miR-891a-5p", + "gene":"EGFR", + "score":0.0684, + "source_name":"mirdip", + "pubmeds":[ + "https://pubmed.ncbi.nlm.nih.gov/5362487", + "https://pubmed.ncbi.nlm.nih.gov/10120249", + "https://pubmed.ncbi.nlm.nih.gov/8948606", + "https://pubmed.ncbi.nlm.nih.gov/5642539", + "https://pubmed.ncbi.nlm.nih.gov/9361765", + "https://pubmed.ncbi.nlm.nih.gov/4895700", + "https://pubmed.ncbi.nlm.nih.gov/5877664", + "https://pubmed.ncbi.nlm.nih.gov/9421182", + "https://pubmed.ncbi.nlm.nih.gov/6998431", + "https://pubmed.ncbi.nlm.nih.gov/7053609", + "https://pubmed.ncbi.nlm.nih.gov/8544338", + "https://pubmed.ncbi.nlm.nih.gov/9253007", + "https://pubmed.ncbi.nlm.nih.gov/10008540" + ], + "sources":[ + "MirAncesTar", + "mirmap_May_2021", + "MiRNATIP" + ], + "score_class":"M" + } + ] + } + ``` - Error Response: - Code: 200 - Content: empty paginated response (`count` = 0) @@ -143,6 +183,27 @@ Receives a miRNA ID (mirbase MIMAT ID or previous ID) and a gene returns the inf - `pubmeds`: array of pubmed for the miRNA-gene interaction (according to mirTaRBase). - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) + - Example: + - URL: http://localhost:8000/mirna-target-interactions?mirna=hsa-miR-3605-3p&gene=MAU2 + - Response: + ```json + { + "id":635935124, + "mirna":"hsa-miR-3605-3p", + "gene":"MAU2", + "score":0.0575, + "source_name":"mirdip", + "pubmeds":[ + + ], + "sources":[ + "MirAncesTar", + "MiRNATIP", + "RNA22" + ], + "score_class":"L" + } + ``` - Error Response: - Code: 404 - Content: - @@ -167,6 +228,26 @@ Returns extra information of a miRNA. - `mirna_sequence`: miRNA nucleotide sequence. - `mirbase_accession_id`: miRNA accession ID (MIMAT). - `links` array of URLs with extra information about this miRNA. + - Example: + - URL: http://localhost:8000/mirna?mirna=hsa-miR-548ai + - Response: + ```json + { + "aliases":[ + "hsa-miR-548ai", + "MIMAT0018989", + "hsa-miR-548ai" + ], + "mirna_sequence":"AAAGGUAAUUGCAGUUUUUCCC", + "mirbase_accession_id":"MIMAT0018989", + "links":[ + { + "source":"mirbase", + "url":"http://www.mirbase.org/cgi-bin/mirna_entry.pl?acc=MIMAT0018989" + } + ] + } + ``` - Error Response: - Code: 404 - Content: - @@ -188,6 +269,22 @@ Returns a paginated response with aliases of a miRNA. - Content: - `mirbase_accession_id`: miRNA mirBase accession ID (MIMAT). - `mature_mirna`: previous ID (according to mirBase). + - Example: + - URL: http://localhost:8000/mirna-aliases?mirbase_accession_id=MIMAT0000062 + - Response: + ```json + { + "count":1, + "next":null, + "previous":null, + "results":[ + { + "mirbase_accession_id":"MIMAT0000062", + "mature_mirna":"hsa-let-7a-5p" + } + ] + } + ``` - Error Response: - @@ -209,6 +306,19 @@ Service that takes a string of any length and returns a list of miRNAs that cont - Success Response: - Code: 200 - Content: a list of miRNAs (IDs or accession IDs from miRbase DB) matching the search criteria. + - Example: + - URL: http://localhost:8000/mirna-codes-finder?query=hsa-let-7a + - Response: + ```json + [ + "hsa-let-7a-3", + "hsa-let-7a-2", + "hsa-let-7a-3p", + "hsa-let-7a-2-3p", + "hsa-let-7a-1", + "hsa-let-7a-5p" + ] + ``` - Error Response: - @@ -229,6 +339,28 @@ Searches for codes from a list of miRNA identifiers and returns the approved acc - Code: 200 - Content: - `mirna_codes`: a JSON object with as many keys as miRNAs in the body of the request. For each miRNA, the value is a valid miRNA accession ID or `null`. + - Example: + - URL: http://localhost:8000/mirna-codes/ + - body: + `{ + "mirna_codes":[ + "name_01", + "Hsa-Mir-935-v2_5p*", + "MIMAT0000066", + "MI0026417", + "hsa-let-7e-5p" + ] + }` + - Response: + ```json + { + "name_01":null, + "Hsa-Mir-935-v2_5p*":null, + "MIMAT0000066":"MIMAT0000066", + "MI0026417":"MI0026417", + "hsa-let-7e-5p":"MIMAT0000066" + } + ``` - Error Response: - Code: 400 - Content: @@ -253,6 +385,18 @@ Service that takes a text string of any length and returns a list of methylation - Success Response: - Code: 200 - Content: a list of methylation sites from the Illumina 'Infinium MethylationEPIC 2.0' array matching the search criteria. + - Example: + - URL: http://localhost:8000/methylation-sites-finder?query=cg25&limit=5 + - Response: + ```json + [ + "cg25324105", + "cg25383568", + "cg25455143", + "cg25459778", + "cg25487775" + ] + ``` - Error Response: - @@ -273,6 +417,26 @@ Searches a list of methylation site names or IDs from different Illumina array v - Code: 200 - Content: - `methylation_sites`: a JSON object with as many keys as methylation names in the body of the request. For each methylation name, the value is a list of valid methylation names to Illumina _Infinium MethylationEPIC 2.0_ array. + - Example: + - URL: http://localhost:8000/methylation-sites/ + - body: + `{ + "methylation_sites":[ + "cg17771854_BC11", + "cg01615704_TC11" + ] + }` + - Response: + ```json + { + "cg17771854_BC11":[ + "cg17771854" + ], + "cg01615704_TC11":[ + "cg01615704" + ] + } + ``` - Error Response: - Code: 400 - Content: @@ -295,7 +459,30 @@ A service that searches from a list of CpG methylation site identifiers from dif - Success Response: - Code: 200 - Content: - - : Returns a Json with as many keys as there are methylation names/ids in the body. For each methylation name/ID, the value is a list of genes that the name/id methylates. + - Returns a Json with as many keys as there are methylation names/ids in the body. For each methylation name/ID, the value is a list of genes that the name/id methylates. + - Example: + - URL: http://localhost:8000/methylation-sites-genes/ + - body: + `{ + "methylation_sites":[ + "cg17771854_BC11", + "cg22461615_TC11", + "name_007" + ] + }` + - Response: + ```json + { + "cg17771854_BC11":[ + "IPO13" + ], + "cg22461615_TC11":[ + "THAP9", + "THAP9-AS1", + "SEC31A" + ] + } + ``` - Error Response: - Code: 400 - Content: @@ -307,6 +494,7 @@ A service that searches from a list of CpG methylation site identifiers from dif Returns a paginated response of diseases related to a miRNA. - URL: `/diseases` +- Method: GET - Required query params: - `mirna`: miRNA (miRNA code or Accession ID) to get its interactions with different targets. If it is not specified, the service returns all the elements in a paginated response. - Functions: @@ -322,6 +510,25 @@ Returns a paginated response of diseases related to a miRNA. - `disease`: disease name. - `pubmed`: Pubmed URL. - `description`: description about why this miRNA is related to this disease. + - Example: + - URL: http://localhost:8000/diseases?mirna=hsa-miR-9500 + - Response: + ```json + { + "count":1, + "next":null, + "previous":null, + "results":[ + { + "id":3540992, + "category":"target gene", + "disease":"Liver Neoplasms", + "pubmed":"https://pubmed.ncbi.nlm.nih.gov/24658401", + "description":"The novel miR-9500 regulates the proliferation and migration of human lung cancer cells by targeting Akt1." + } + ] + } + ``` - Error Response: - Code: 200 - Content: empty paginated response (number of elements = 0) @@ -333,6 +540,7 @@ Returns a paginated response of diseases related to a miRNA. Returns a paginated response of experimentally validated small molecules (or drugs) effects on miRNA expression. - URL: `/drugs` +- Method: GET - Required query params: - `mirna`: miRNA (miRNA code or Accession ID) to get its interactions with different targets. If it is not specified, the service returns all the elements in a paginated response. - Functions: @@ -353,6 +561,29 @@ Returns a paginated response of experimentally validated small molecules (or dru - `reference`: reference title. - `expression_pattern`: expression pattern of miRNA. - `support`: support information for this effect. + - Example: + - URL: http://localhost:8000/drugs?mirna=miR-126* + - Response: + ```json + { + "count":1, + "next":null, + "previous":null, + "results":[ + { + "id":275028, + "small_molecule":"17beta-estradiol (E2)", + "fda_approved":true, + "detection_method":"Microarray", + "condition":"MCF-7AKT breast cancer cells", + "pubmed":"https://pubmed.ncbi.nlm.nih.gov/19528081", + "reference":"Estradiol-regulated microRNAs control estradiol response in breast cancer cells.", + "expression_pattern":"down-regulated", + "support":"To investigate this possibility, we determined microRNA-expression patterns in MCF-7p and MCF-7AKT cells with and without E2 treatment for 4 h. We observed 21 E2-inducible and 7 E2-repressible microRNAs in MCF-7p cells (statistical cutoff P-value <0.05 and fold change >1.5 or <0.7) (Table 1)." + } + ] + } + ``` - Error Response: - Code: 200 - Content: empty paginated response (number of elements = 0) From c5cd4c539ae0577b5cd8344864a0cadc4bb4016a Mon Sep 17 00:00:00 2001 From: mauricio Date: Thu, 19 Oct 2023 17:04:38 -0300 Subject: [PATCH 02/11] examples added --- README.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index bd4769a..b491588 100644 --- a/README.md +++ b/README.md @@ -342,7 +342,8 @@ Searches for codes from a list of miRNA identifiers and returns the approved acc - Example: - URL: http://localhost:8000/mirna-codes/ - body: - `{ + ```json + { "mirna_codes":[ "name_01", "Hsa-Mir-935-v2_5p*", @@ -350,7 +351,8 @@ Searches for codes from a list of miRNA identifiers and returns the approved acc "MI0026417", "hsa-let-7e-5p" ] - }` + } + ``` - Response: ```json { @@ -420,12 +422,14 @@ Searches a list of methylation site names or IDs from different Illumina array v - Example: - URL: http://localhost:8000/methylation-sites/ - body: - `{ + ```json + { "methylation_sites":[ "cg17771854_BC11", "cg01615704_TC11" ] - }` + } + ``` - Response: ```json { @@ -463,13 +467,15 @@ A service that searches from a list of CpG methylation site identifiers from dif - Example: - URL: http://localhost:8000/methylation-sites-genes/ - body: - `{ + ```json + { "methylation_sites":[ "cg17771854_BC11", "cg22461615_TC11", "name_007" ] - }` + } + ``` - Response: ```json { From cba5f7b82544491730d1149366df960036a96bab Mon Sep 17 00:00:00 2001 From: mauricio Date: Fri, 20 Oct 2023 15:42:02 -0300 Subject: [PATCH 03/11] Methylation details endpoint added --- ModulectorBackend/urls.py | 1 + .../0039_alter_methylationepic_id.py | 18 +++ modulector/models.py | 1 + modulector/templates/index.html | 3 + modulector/views.py | 116 +++++++++++++----- 5 files changed, 110 insertions(+), 29 deletions(-) create mode 100644 modulector/migrations/0039_alter_methylationepic_id.py diff --git a/ModulectorBackend/urls.py b/ModulectorBackend/urls.py index 93094c6..845f35e 100644 --- a/ModulectorBackend/urls.py +++ b/ModulectorBackend/urls.py @@ -34,6 +34,7 @@ path('drugs/', views.MirnaDrugsList.as_view(), name='drugs'), path('subscribe-pubmeds/', views.SubscribeUserToPubmed.as_view()), path('unsubscribe-pubmeds/', views.UnsubscribeUserToPubmed.as_view()), + path('methylation/', views.MethylationDetails.as_view(), name='methylation'), path('methylation-sites-finder/', views.MethylationSitesFinder.as_view(), name='methylation_sites_finder'), path('methylation-sites/', views.MethylationSites.as_view(), diff --git a/modulector/migrations/0039_alter_methylationepic_id.py b/modulector/migrations/0039_alter_methylationepic_id.py new file mode 100644 index 0000000..9d126e9 --- /dev/null +++ b/modulector/migrations/0039_alter_methylationepic_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.2 on 2023-10-20 17:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('modulector', '0038_alter_methylationepic_epicv1_loci_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='methylationepic', + name='id', + field=models.IntegerField(db_index=True, primary_key=True, serialize=False), + ), + ] diff --git a/modulector/models.py b/modulector/models.py index 4c80fd3..6043d62 100644 --- a/modulector/models.py +++ b/modulector/models.py @@ -14,6 +14,7 @@ class DatasetSeparator(models.TextChoices): # Modelos para Methylation class MethylationEPIC(models.Model): + id = models.IntegerField(primary_key=True, blank=False, null=False, db_index=True) ilmnid = models.CharField( max_length=25, blank=False, null=False, db_index=True) name = models.CharField(max_length=15, blank=False, diff --git a/modulector/templates/index.html b/modulector/templates/index.html index 22293d7..aaf3507 100644 --- a/modulector/templates/index.html +++ b/modulector/templates/index.html @@ -31,6 +31,9 @@

Modulector v{{ version }}

  • Drugs
  • +
  • + Methylation Details +
  • Illumina Methylation Sites Finder
  • diff --git a/modulector/views.py b/modulector/views.py index 6d51d42..61e238a 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -10,7 +10,7 @@ from rest_framework.exceptions import ParseError from rest_framework.response import Response from rest_framework.views import APIView -from modulector.models import MethylationUCSCRefGene, MirnaXGene, Mirna, MirbaseIdMirna, MirnaDisease, MirnaDrug, GeneAliases, MethylationEPIC +from modulector.models import MethylationUCSC_CPGIsland, MethylationUCSCRefGene, MirnaXGene, Mirna, MirbaseIdMirna, MirnaDisease, MirnaDrug, GeneAliases, MethylationEPIC from modulector.pagination import StandardResultsSetPagination from modulector.serializers import MirnaXGenSerializer, MirnaSerializer, \ MirnaAliasesSerializer, MirnaDiseaseSerializer, MirnaDrugsSerializer, get_mirna_from_accession, \ @@ -29,7 +29,7 @@ PROCESS_POOL_WORKERS = settings.PROCESS_POOL_WORKERS -def get_methylation_epic_sites(input_id: str) -> List[str]: +def get_methylation_epic_sites_names(input_id: str) -> List[str]: """ Gets methylation sites from any type of Loci id. :param input_id: String to query in the DB. @@ -58,6 +58,27 @@ def get_limit_parameter(value: Optional[str]) -> int: # Returns default in case of non-numeric parameter return DEFAULT_PAGE_SIZE +def get_methylation_epic_sites_ids(input_name: str) -> List[str]: + """ + Gets methylation sites from any type of Loci id + :param input_name: String to query in the DB (site name) + :return: List of ID of Methylation sites from EPIC v2 database + """ + res = MethylationEPIC.objects.filter(Q(ilmnid=input_name) | Q(name=input_name) | + Q(methyl450_loci=input_name) | Q(methyl27_loci=input_name) | + Q(epicv1_loci=input_name)).values_list('id', flat=True) + return list(res) + +def get_genes_from_methylation_epic_site(input_id: str) -> List[str]: + """ + Gets genes from a specific methylation CpG site + :param input_id: String to query in the DB (CpG ID) + :return: Gene for the given input + """ + gene = MethylationUCSCRefGene.objects.filter( + Q(methylation_epic_v2_ilmnid=input_id)).values_list('ucsc_refgene_name', flat=True) + return list(gene) + class MirnaTargetInteractions(viewsets.ReadOnlyModelViewSet): """Returns a single instance with data about an interaction between a miRNA and a gene @@ -308,7 +329,7 @@ def post(request): res = { methylation_name: result for methylation_name, result in zip(methylation_sites, executor.map( - get_methylation_epic_sites, methylation_sites + get_methylation_epic_sites_names, methylation_sites )) } @@ -336,29 +357,6 @@ class MethylationSitesToGenes(APIView): """A service that searches a list of CpG methylation site identifiers from different versions of Illumina arrays and returns the gene(s) they belong to.""" - @staticmethod - def __get_methylation_epic_sites(input_name: str) -> List[str]: - """ - Gets methylation sites from any type of Loci id - :param input_name: String to query in the DB (site name) - :return: List of ID of Methylation sites from EPIC v2 database - """ - res = MethylationEPIC.objects.filter(Q(ilmnid=input_name) | Q(name=input_name) | - Q(methyl450_loci=input_name) | Q(methyl27_loci=input_name) | - Q(epicv1_loci=input_name)).values_list('id', flat=True) - return list(res) - - @staticmethod - def __get_genes_from_methylation_epic_site(input_id: str) -> List[str]: - """ - Gets genes from a specific methylation CpG site - :param input_id: String to query in the DB (CpG ID) - :return: Gene for the given input - """ - gene = MethylationUCSCRefGene.objects.filter( - Q(methylation_epic_v2_ilmnid=input_id)).values_list('ucsc_refgene_name', flat=True) - return list(gene) - def post(self, request): data = request.data if "methylation_sites" not in data: @@ -372,12 +370,12 @@ def post(self, request): for methylation_name in methylation_sites: res[methylation_name] = [] # For each CpG methylation site passed as a parameter... I look for its Identifier in the version of the EPIC v2 array: - epics_ids = self.__get_methylation_epic_sites(methylation_name) + epics_ids = get_methylation_epic_sites_ids(methylation_name) for site_id in epics_ids: # For each identifier in the EPIC v2 array, I search for the genes involved: - genes_list = self.__get_genes_from_methylation_epic_site( + genes_list = get_genes_from_methylation_epic_site( site_id) - + [res[methylation_name].append( gen) for gen in genes_list if gen not in res[methylation_name]] @@ -386,6 +384,66 @@ def post(self, request): return Response(res) +class MethylationDetails(APIView): + """Service that obtains information about a specific CpG methylation site from + the 'Infinium MethylationEPIC V2.0' array.""" + + def get(self, _request): + methylation_site = self.request.GET.get('methylation_site') + if not methylation_site: + return Response(status=404, data={"'methylation_site' is mandatory"}) + + res = {} + # search for id in array + epic_data = MethylationEPIC.objects.filter(Q(name=methylation_site)).first() + + if epic_data: + # load name to response + res["name"] = epic_data.name + + # load chomosomic data + if epic_data.strand_fr == "F": + res["chromosome_position"] = epic_data.chr +":"+ str(epic_data.mapinfo) + " [+]" + elif epic_data.strand_fr == "R": + res["chromosome_position"] = epic_data.chr +":"+ str(epic_data.mapinfo) + " [-]" + + # load aliases to response + res["aliases"] = [] + if epic_data.methyl27_loci and epic_data.methyl27_loci != epic_data.name: + res["aliases"].append(epic_data.methyl27_loci) + if epic_data.methyl450_loci and epic_data.methyl450_loci != epic_data.name: + res["aliases"].append(epic_data.methyl450_loci) + if epic_data.epicv1_loci and epic_data.epicv1_loci != epic_data.name: + res["aliases"].append(epic_data.epicv1_loci) + if epic_data.ilmnid and epic_data.ilmnid != epic_data.name: + res["aliases"].append(epic_data.ilmnid) + + # search and loads genes + genes_list_with_dup = get_genes_from_methylation_epic_site(epic_data.id) + # remove duplicates + gene_list = [] + [gene_list.append(x) for x in genes_list_with_dup if x not in gene_list] + res["genes"] = gene_list + + # searches and loads for islands relations + islands_data = MethylationUCSC_CPGIsland.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) + res["ucsc_cpg_islands"] = [] + for island in islands_data: + res["ucsc_cpg_islands"].append({"cpg_island": island.ucsc_cpg_island_name, "relation": island.relation_to_ucsc_cpg_island}) + + # searches and loads for genes relations + genes_data = MethylationUCSCRefGene.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) + res["relation_to_genes"] = {} + for gene in genes_data: + if gene.ucsc_refgene_name not in res["relation_to_genes"]: + res["relation_to_genes"][gene.ucsc_refgene_name]=[] + if gene.ucsc_refgene_group not in res["relation_to_genes"][gene.ucsc_refgene_name]: + res["relation_to_genes"][gene.ucsc_refgene_name].append(gene.ucsc_refgene_group) + + return Response(res) + else: + return Response(status=400, data={ methylation_site + " is not a valid methylation site"}) + def index(request): return render(request, 'index.html', {'version': settings.VERSION}) From 5788256368bcbf1c43c427241a2ba2eb2bdd0d18 Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 21 Oct 2023 08:55:28 -0300 Subject: [PATCH 04/11] changes in methylation endpoint --- modulector/views.py | 65 +++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/modulector/views.py b/modulector/views.py index 61e238a..e9315ea 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -58,27 +58,6 @@ def get_limit_parameter(value: Optional[str]) -> int: # Returns default in case of non-numeric parameter return DEFAULT_PAGE_SIZE -def get_methylation_epic_sites_ids(input_name: str) -> List[str]: - """ - Gets methylation sites from any type of Loci id - :param input_name: String to query in the DB (site name) - :return: List of ID of Methylation sites from EPIC v2 database - """ - res = MethylationEPIC.objects.filter(Q(ilmnid=input_name) | Q(name=input_name) | - Q(methyl450_loci=input_name) | Q(methyl27_loci=input_name) | - Q(epicv1_loci=input_name)).values_list('id', flat=True) - return list(res) - -def get_genes_from_methylation_epic_site(input_id: str) -> List[str]: - """ - Gets genes from a specific methylation CpG site - :param input_id: String to query in the DB (CpG ID) - :return: Gene for the given input - """ - gene = MethylationUCSCRefGene.objects.filter( - Q(methylation_epic_v2_ilmnid=input_id)).values_list('ucsc_refgene_name', flat=True) - return list(gene) - class MirnaTargetInteractions(viewsets.ReadOnlyModelViewSet): """Returns a single instance with data about an interaction between a miRNA and a gene @@ -357,6 +336,29 @@ class MethylationSitesToGenes(APIView): """A service that searches a list of CpG methylation site identifiers from different versions of Illumina arrays and returns the gene(s) they belong to.""" + @staticmethod + def __get_methylation_epic_sites_ids(input_name: str) -> List[str]: + """ + Gets methylation sites from any type of Loci id + :param input_name: String to query in the DB (site name) + :return: List of ID of Methylation sites from EPIC v2 database + """ + res = MethylationEPIC.objects.filter(Q(ilmnid=input_name) | Q(name=input_name) | + Q(methyl450_loci=input_name) | Q(methyl27_loci=input_name) | + Q(epicv1_loci=input_name)).values_list('id', flat=True) + return list(res) + + @staticmethod + def __get_genes_from_methylation_epic_site(input_id: str) -> List[str]: + """ + Gets genes from a specific methylation CpG site + :param input_id: String to query in the DB (CpG ID) + :return: Gene for the given input + """ + gene = MethylationUCSCRefGene.objects.filter( + Q(methylation_epic_v2_ilmnid=input_id)).values_list('ucsc_refgene_name', flat=True) + return list(gene) + def post(self, request): data = request.data if "methylation_sites" not in data: @@ -370,10 +372,10 @@ def post(self, request): for methylation_name in methylation_sites: res[methylation_name] = [] # For each CpG methylation site passed as a parameter... I look for its Identifier in the version of the EPIC v2 array: - epics_ids = get_methylation_epic_sites_ids(methylation_name) + epics_ids = self.__get_methylation_epic_sites_ids(methylation_name) for site_id in epics_ids: # For each identifier in the EPIC v2 array, I search for the genes involved: - genes_list = get_genes_from_methylation_epic_site( + genes_list = self.__get_genes_from_methylation_epic_site( site_id) [res[methylation_name].append( @@ -417,13 +419,6 @@ def get(self, _request): res["aliases"].append(epic_data.epicv1_loci) if epic_data.ilmnid and epic_data.ilmnid != epic_data.name: res["aliases"].append(epic_data.ilmnid) - - # search and loads genes - genes_list_with_dup = get_genes_from_methylation_epic_site(epic_data.id) - # remove duplicates - gene_list = [] - [gene_list.append(x) for x in genes_list_with_dup if x not in gene_list] - res["genes"] = gene_list # searches and loads for islands relations islands_data = MethylationUCSC_CPGIsland.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) @@ -433,12 +428,12 @@ def get(self, _request): # searches and loads for genes relations genes_data = MethylationUCSCRefGene.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) - res["relation_to_genes"] = {} + res["genes"] = {} for gene in genes_data: - if gene.ucsc_refgene_name not in res["relation_to_genes"]: - res["relation_to_genes"][gene.ucsc_refgene_name]=[] - if gene.ucsc_refgene_group not in res["relation_to_genes"][gene.ucsc_refgene_name]: - res["relation_to_genes"][gene.ucsc_refgene_name].append(gene.ucsc_refgene_group) + if gene.ucsc_refgene_name not in res["genes"]: + res["genes"][gene.ucsc_refgene_name]=[] + if gene.ucsc_refgene_group not in res["genes"][gene.ucsc_refgene_name]: + res["genes"][gene.ucsc_refgene_name].append(gene.ucsc_refgene_group) return Response(res) else: From c439a4a8371875d5e3ff7df6dba97583e188a26c Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 21 Oct 2023 09:23:33 -0300 Subject: [PATCH 05/11] documentation --- README.md | 59 +++++++++++++++++++++++++++++++++++++++++++++ modulector/views.py | 2 +- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b491588..a0e88d6 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Modulector is a performing open platform that provides information about miRNAs - [Methylation sites finder](#methylation-sites-finder) - [Methylation sites](#methylation-sites) - [Genes of methylation sites](#genes-of-methylation-sites) + - [Methylation site details](#methylation-site-details) - [Diseases](#diseases) - [Drugs](#drugs) - [Subscribe to PUBMEDS news](#subscribe-to-pubmeds-news) @@ -495,6 +496,64 @@ A service that searches from a list of CpG methylation site identifiers from dif - `detail`: a text with information about the error. +### Methylation site details + +Returns information of a methylation site. + +- URL: `/methylation` +- Required query params: + - `methylation_site`: methylation_site name from Illumina _Infinium MethylationEPIC 2.0_ array +- Functions: + - Ordering fields: ordering is not available for this service + - Filtering fields: filtering is not available for this service + - Searching fields: searching is not available for this service + - Pagination: no +- Success Response: + - Code: 200 + - Content: + - `name`: name of methylation site. + - `aliases`: list of other names for the same methylation site on other illumina arrays (EPIC v2, EPIC v1, Methyl450 and Methyl27). + - `chromosome_position`: information about the chromosome, position and strand on which the site is located. + - `ucsc_cpg_islands`: List of islands related to the methylation site according to the UCSC database. Each element in the view is a json with the following content: + - `cpg_island`: chromosomal coordinates where the island is located. + - `relation`: Relation of the site to the CpG island. The values it can take are: *Island*=within boundaries of a CpG Island, *N_Shore*=0-2kb 5' of Island, *N_Shelf*=2kb-4kb 5' of Island, *S_Shore*=0-2kb 3' of Island, *S_Shelf*=2kb-4kb 3' of Island. + - `genes`: The value is a json where each key is a gene that is related to the methylation site. The values for each gene is a list that contains the region of the gene where the methylation site is located. These regions, according to the NCBI RefSeq database, can be: *5UTR*=5' untranslated region between the TSS and ATG start site, *3UTR*=3' untranslated region between stop codon and poly A signal, *exon_#*, *TSS200*=1-200 bp 5' the TSS, *TS1500*=200-1500 bp 5' of the TSS. + - Example: + - URL: http://localhost:8000/methylation?methylation_site=cg22461615 + - Response: + ```json + { + "name":"cg22461615", + "chromosome_position":"chr4:82900764 [+]", + "aliases":[ + "cg22461615_TC11" + ], + "ucsc_cpg_islands":[ + { + "cpg_island":"chr4:82900535-82900912", + "relation":"Island" + } + ], + "genes":{ + "THAP9":[ + "5UTR", + "exon_1" + ], + "THAP9-AS1":[ + "exon_1" + ], + "SEC31A":[ + "TSS200" + ] + } + } + ``` + *NOTE*: Multiple values of the same gene name indicate splice variants. +- Error Response: + - Code: 400 + - Content: error explanation text + + ### Diseases Returns a paginated response of diseases related to a miRNA. diff --git a/modulector/views.py b/modulector/views.py index e9315ea..c0308c7 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -393,7 +393,7 @@ class MethylationDetails(APIView): def get(self, _request): methylation_site = self.request.GET.get('methylation_site') if not methylation_site: - return Response(status=404, data={"'methylation_site' is mandatory"}) + return Response(status=400, data={"'methylation_site' is mandatory"}) res = {} # search for id in array From 4142c0c7d78e561a132dc486cffce236062f70bb Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 21 Oct 2023 11:16:32 -0300 Subject: [PATCH 06/11] mirna-interactions endpoint updated --- README.md | 18 ++++++++++------- modulector/views.py | 48 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a0e88d6..b7a8489 100644 --- a/README.md +++ b/README.md @@ -94,15 +94,19 @@ All of the above parameters can be used together! For example, if we wanted to c ### MiRNA interactions -Receives a miRNA ID (mirbase MIMAT ID or previous ID) and returns a paginated vector. Each vector entry represents a miRNA-Gene interaction. +Receives a miRNA and/or a gene symbol and returns a paginated vector. Each vector entry represents a miRNA-Gene interaction. +If no gene symbol is entered, all mirna interactions are returned. If a mirna is not entered, all gene interactions are returned. If both are entered, the interaction of mirna with the gene is returned. - URL: `/mirna-interactions` -- Required query params: - - `mirna`: miRNA (miRNA code or Accession ID) to get its interactions with different targets +- Query params: + - `mirna`: miRNA (Accession ID or name in mirBase) to get its interactions with different genes targets. + - `gene`: gene symbol to get its interactions with different miRNAs targets. + - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). +*NOTE*: mirna or gene are required - Functions: - Ordering fields: `gene` and `score` - Filtering fields: filtering is not available for this service - - Searching fields: `gene` + - Searching fields: `gene` - Pagination: yes - Success Response: - Code: 200 @@ -116,7 +120,7 @@ Receives a miRNA ID (mirbase MIMAT ID or previous ID) and returns a paginated ve - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) - Example: - - URL: http://localhost:8000/mirna-interactions?mirna=hsa-miR-891a-5p&search=EGFR + - URL: http://localhost:8000/mirna-interactions?mirna=hsa-miR-891a-5p&gene=EGFR - Response: ```json { @@ -156,8 +160,8 @@ Receives a miRNA ID (mirbase MIMAT ID or previous ID) and returns a paginated ve } ``` - Error Response: - - Code: 200 - - Content: empty paginated response (`count` = 0) + - Code: 400 + - Content: `detail`: error description ### MiRNA target interactions diff --git a/modulector/views.py b/modulector/views.py index c0308c7..48507c8 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -108,14 +108,50 @@ class MirnaInteractions(generics.ListAPIView): search_fields = ['gene'] handler400 = 'rest_framework.exceptions.bad_request' - def get_queryset(self): - mirna = self.request.GET.get("mirna") + @staticmethod + def __get_gene_aliases(gene: str) -> List[str]: + """Retrieves the aliases for a gene based on the gene provided""" + match_gene = GeneAliases.objects.filter( + Q(alias=gene) | Q(gene_symbol=gene)).first() + if match_gene is None: + return [] - if not mirna: - raise ParseError(detail="mirna is obligatory") + gene_symbol = match_gene.gene_symbol + aliases = list(GeneAliases.objects.filter( + gene_symbol=gene_symbol).values_list('alias', flat=True).distinct()) + # Adds the parameter to not omit it in the future search + aliases.append(gene) + return aliases - mirna_aliases = get_mirna_aliases(mirna) - return MirnaXGene.objects.filter(mirna__mirna_code__in=mirna_aliases) + def get_queryset(self): + mirna = self.request.GET.get("mirna") + gene = self.request.GET.get("gene") + score = self.request.GET.get("score") + + if score: + try: + score = float(score) + if not 0 <= score <= 1: + raise ParseError(detail="the 'score' value must be between 0 and 1") + except ValueError: + raise ParseError(detail="'score' must be a numerical value between 0 and 1") + + if not mirna and not gene: + raise ParseError(detail="'mirna' or 'gene' are mandatory") + elif mirna and not gene: # only mirna + mirna_aliases = get_mirna_aliases(mirna) + data = MirnaXGene.objects.filter(mirna__mirna_code__in=mirna_aliases) + elif not mirna and gene: # only gene + gene_aliases = self.__get_gene_aliases(gene) + data = MirnaXGene.objects.filter(gene__in=gene_aliases) + else: # mirna and gene + # Gets gene aliases + gene_aliases = self.__get_gene_aliases(gene) + # Gets miRNA aliases + mirna_aliases = get_mirna_aliases(mirna) + data = MirnaXGene.objects.filter(mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) + + return data.filter(score__gte=score) if score else data class MirnaAliasesList(generics.ListAPIView): From 545fe0c0ec6cd077356ab259e7ddc16a7ed4955e Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 21 Oct 2023 11:18:29 -0300 Subject: [PATCH 07/11] documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b7a8489..49e8501 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is - Query params: - `mirna`: miRNA (Accession ID or name in mirBase) to get its interactions with different genes targets. - `gene`: gene symbol to get its interactions with different miRNAs targets. - - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). + - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). *NOTE*: mirna or gene are required - Functions: - Ordering fields: `gene` and `score` From fef3233e8040df11a122d363a1add2ea32103512 Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 21 Oct 2023 11:39:47 -0300 Subject: [PATCH 08/11] PEP8 corrections --- modulector/views.py | 64 +++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/modulector/views.py b/modulector/views.py index 48507c8..d6896c9 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -132,24 +132,28 @@ def get_queryset(self): try: score = float(score) if not 0 <= score <= 1: - raise ParseError(detail="the 'score' value must be between 0 and 1") + raise ParseError( + detail="the 'score' value must be between 0 and 1") except ValueError: - raise ParseError(detail="'score' must be a numerical value between 0 and 1") + raise ParseError( + detail="'score' must be a numerical value between 0 and 1") if not mirna and not gene: raise ParseError(detail="'mirna' or 'gene' are mandatory") - elif mirna and not gene: # only mirna + elif mirna and not gene: # only mirna mirna_aliases = get_mirna_aliases(mirna) - data = MirnaXGene.objects.filter(mirna__mirna_code__in=mirna_aliases) - elif not mirna and gene: # only gene + data = MirnaXGene.objects.filter( + mirna__mirna_code__in=mirna_aliases) + elif not mirna and gene: # only gene gene_aliases = self.__get_gene_aliases(gene) data = MirnaXGene.objects.filter(gene__in=gene_aliases) - else: # mirna and gene + else: # mirna and gene # Gets gene aliases gene_aliases = self.__get_gene_aliases(gene) # Gets miRNA aliases mirna_aliases = get_mirna_aliases(mirna) - data = MirnaXGene.objects.filter(mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) + data = MirnaXGene.objects.filter( + mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) return data.filter(score__gte=score) if score else data @@ -189,7 +193,7 @@ def post(self, request): return Response({"detail": "'mirna_codes' is mandatory"}, status=status.HTTP_400_BAD_REQUEST) mirna_codes = data["mirna_codes"] - if type(mirna_codes) != list: + if not isinstance(mirna_codes, list): return Response({"detail": "'mirna_codes' must be of list type"}, status=status.HTTP_400_BAD_REQUEST) res = {} @@ -334,11 +338,11 @@ def post(request): return Response({"detail": "'methylation_sites' is mandatory"}, status=status.HTTP_400_BAD_REQUEST) methylation_sites = data["methylation_sites"] - if type(methylation_sites) != list: + if not isinstance(methylation_sites, list): return Response({"detail": "'methylation_sites' must be of list type"}, status=status.HTTP_400_BAD_REQUEST) # Generates a dict with the methylation sites as keys and the result of the query as values. - # NOTE: it uses ProcessPoolExecutor to parallelize the queries and not a ThreadPoolExecutor because + # note: it uses ProcessPoolExecutor to parallelize the queries and not a ThreadPoolExecutor because # the latter has a bug closing Django connections (see https://stackoverflow.com/q/57211476/7058363) with ProcessPoolExecutor(max_workers=PROCESS_POOL_WORKERS) as executor: res = { @@ -369,7 +373,7 @@ def get(self, _request): class MethylationSitesToGenes(APIView): - """A service that searches a list of CpG methylation site identifiers from different + """A service that searches a list of CpG methylation site identifiers from different versions of Illumina arrays and returns the gene(s) they belong to.""" @staticmethod @@ -383,7 +387,7 @@ def __get_methylation_epic_sites_ids(input_name: str) -> List[str]: Q(methyl450_loci=input_name) | Q(methyl27_loci=input_name) | Q(epicv1_loci=input_name)).values_list('id', flat=True) return list(res) - + @staticmethod def __get_genes_from_methylation_epic_site(input_id: str) -> List[str]: """ @@ -401,7 +405,7 @@ def post(self, request): return Response({"detail": "'methylation_sites' is mandatory"}, status=status.HTTP_400_BAD_REQUEST) methylation_sites = data["methylation_sites"] - if type(methylation_sites) != list: + if not isinstance(methylation_sites, list): return Response({"detail": "'methylation_sites' must be of list type"}, status=status.HTTP_400_BAD_REQUEST) res = {} @@ -413,7 +417,7 @@ def post(self, request): # For each identifier in the EPIC v2 array, I search for the genes involved: genes_list = self.__get_genes_from_methylation_epic_site( site_id) - + [res[methylation_name].append( gen) for gen in genes_list if gen not in res[methylation_name]] @@ -422,6 +426,7 @@ def post(self, request): return Response(res) + class MethylationDetails(APIView): """Service that obtains information about a specific CpG methylation site from the 'Infinium MethylationEPIC V2.0' array.""" @@ -433,17 +438,20 @@ def get(self, _request): res = {} # search for id in array - epic_data = MethylationEPIC.objects.filter(Q(name=methylation_site)).first() - + epic_data = MethylationEPIC.objects.filter( + Q(name=methylation_site)).first() + if epic_data: # load name to response res["name"] = epic_data.name # load chomosomic data if epic_data.strand_fr == "F": - res["chromosome_position"] = epic_data.chr +":"+ str(epic_data.mapinfo) + " [+]" + res["chromosome_position"] = epic_data.chr + \ + ":" + str(epic_data.mapinfo) + " [+]" elif epic_data.strand_fr == "R": - res["chromosome_position"] = epic_data.chr +":"+ str(epic_data.mapinfo) + " [-]" + res["chromosome_position"] = epic_data.chr + \ + ":" + str(epic_data.mapinfo) + " [-]" # load aliases to response res["aliases"] = [] @@ -452,28 +460,32 @@ def get(self, _request): if epic_data.methyl450_loci and epic_data.methyl450_loci != epic_data.name: res["aliases"].append(epic_data.methyl450_loci) if epic_data.epicv1_loci and epic_data.epicv1_loci != epic_data.name: - res["aliases"].append(epic_data.epicv1_loci) + res["aliases"].append(epic_data.epicv1_loci) if epic_data.ilmnid and epic_data.ilmnid != epic_data.name: res["aliases"].append(epic_data.ilmnid) # searches and loads for islands relations - islands_data = MethylationUCSC_CPGIsland.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) + islands_data = MethylationUCSC_CPGIsland.objects.filter( + Q(methylation_epic_v2_ilmnid=epic_data.id)) res["ucsc_cpg_islands"] = [] for island in islands_data: - res["ucsc_cpg_islands"].append({"cpg_island": island.ucsc_cpg_island_name, "relation": island.relation_to_ucsc_cpg_island}) + res["ucsc_cpg_islands"].append( + {"cpg_island": island.ucsc_cpg_island_name, "relation": island.relation_to_ucsc_cpg_island}) # searches and loads for genes relations - genes_data = MethylationUCSCRefGene.objects.filter(Q(methylation_epic_v2_ilmnid=epic_data.id)) + genes_data = MethylationUCSCRefGene.objects.filter( + Q(methylation_epic_v2_ilmnid=epic_data.id)) res["genes"] = {} for gene in genes_data: if gene.ucsc_refgene_name not in res["genes"]: - res["genes"][gene.ucsc_refgene_name]=[] + res["genes"][gene.ucsc_refgene_name] = [] if gene.ucsc_refgene_group not in res["genes"][gene.ucsc_refgene_name]: - res["genes"][gene.ucsc_refgene_name].append(gene.ucsc_refgene_group) - + res["genes"][gene.ucsc_refgene_name].append( + gene.ucsc_refgene_group) + return Response(res) else: - return Response(status=400, data={ methylation_site + " is not a valid methylation site"}) + return Response(status=400, data={methylation_site + " is not a valid methylation site"}) def index(request): From 8d33a5914f7e315f8233b89ebefd802fdea4030c Mon Sep 17 00:00:00 2001 From: mauricio Date: Sat, 28 Oct 2023 09:31:38 -0300 Subject: [PATCH 09/11] mirna-interactions endpoint deleted --- ModulectorBackend/urls.py | 4 +- README.md | 61 ++------------ docker-compose.dev.yml | 2 +- ..._mirnaxgene_gene_alter_mirnaxgene_score.py | 23 +++++ modulector/models.py | 9 +- modulector/templates/index.html | 3 - modulector/views.py | 84 +++++++++---------- 7 files changed, 77 insertions(+), 109 deletions(-) create mode 100644 modulector/migrations/0040_alter_mirnaxgene_gene_alter_mirnaxgene_score.py diff --git a/ModulectorBackend/urls.py b/ModulectorBackend/urls.py index 845f35e..42d7ab8 100644 --- a/ModulectorBackend/urls.py +++ b/ModulectorBackend/urls.py @@ -22,10 +22,8 @@ urlpatterns = [ path('admin/', admin.site.urls), path('mirna/', views.MirnaList.as_view({'get': 'list'}), name='mirna'), - path('mirna-target-interactions/', views.MirnaTargetInteractions.as_view({'get': 'list'}), + path('mirna-target-interactions/', views.MirnaTargetInteractions.as_view(), name='mirna_target_interactions'), - path('mirna-interactions/', views.MirnaInteractions.as_view(), - name='mirna_interactions'), path('mirna-aliases/', views.MirnaAliasesList.as_view(), name='mirna_aliases'), path('mirna-codes/', views.MirnaCodes.as_view(), name='mirna_codes'), path('mirna-codes-finder/', views.MirnaCodesFinder.as_view(), diff --git a/README.md b/README.md index 49e8501..0294e11 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Modulector is a performing open platform that provides information about miRNAs - [Pagination](#pagination) - [Combining functions](#combining-functions) - [Services](#services) - - [MiRNA interactions](#mirna-interactions) - [MiRNA target interactions](#mirna-target-interactions) - [MiRNA details](#mirna-details) - [MiRNA aliases](#mirna-aliases) @@ -92,16 +91,16 @@ All of the above parameters can be used together! For example, if we wanted to c ## Services -### MiRNA interactions +### MiRNA target interactions Receives a miRNA and/or a gene symbol and returns a paginated vector. Each vector entry represents a miRNA-Gene interaction. If no gene symbol is entered, all mirna interactions are returned. If a mirna is not entered, all gene interactions are returned. If both are entered, the interaction of mirna with the gene is returned. -- URL: `/mirna-interactions` +- URL: `/mirna-target-interactions` - Query params: - `mirna`: miRNA (Accession ID or name in mirBase) to get its interactions with different genes targets. - `gene`: gene symbol to get its interactions with different miRNAs targets. - - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). + - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). The value of this score is provided by the mirDip database. *NOTE*: mirna or gene are required - Functions: - Ordering fields: `gene` and `score` @@ -116,11 +115,11 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is - `gene`: target gene. - `score`: interaction score (according mirDIP). - `source_name`: database from which the interaction was extracted. - - `pubmeds`: array of pubmed for the miRNA-gene interaction (according to mirTaRBase). + - `pubmeds`: array of pubmed for the miRNA-gene interaction (according to mirTaRBase). - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) - Example: - - URL: http://localhost:8000/mirna-interactions?mirna=hsa-miR-891a-5p&gene=EGFR + - URL: http://localhost:8000/mirna-target-interactions?mirna=hsa-miR-891a-5p&gene=EGFR - Response: ```json { @@ -164,56 +163,6 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is - Content: `detail`: error description -### MiRNA target interactions - -Receives a miRNA ID (mirbase MIMAT ID or previous ID) and a gene returns the information about its interaction, including related publications and the interaction score. - -- URL: `/mirna-target-interactions` -- Required query params: - - `mirna`: miRNA identifier (miRNA code or Accession ID) - - `gene`: gene symbol -- Functions: - - Ordering fields: ordering is not available for this service - - Filtering fields: filtering is not available for this service - - Searching fields: searching is not available for this service - - Pagination: no -- Success Response: - - Code: 200 - - Content: - - `id`: internal ID of the interaction. - - `mirna`: miRNA ID (mirbase MIMAT id or previous ID). The received one as query param. - - `gene`: target gene. - - `score`: interaction score (according mirDIP). - - `source_name`: database from which the interaction was extracted. - - `pubmeds`: array of pubmed for the miRNA-gene interaction (according to mirTaRBase). - - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) - - Example: - - URL: http://localhost:8000/mirna-target-interactions?mirna=hsa-miR-3605-3p&gene=MAU2 - - Response: - ```json - { - "id":635935124, - "mirna":"hsa-miR-3605-3p", - "gene":"MAU2", - "score":0.0575, - "source_name":"mirdip", - "pubmeds":[ - - ], - "sources":[ - "MirAncesTar", - "MiRNATIP", - "RNA22" - ], - "score_class":"L" - } - ``` -- Error Response: - - Code: 404 - - Content: - - - ### MiRNA details Returns extra information of a miRNA. diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 842b808..36ab396 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -7,7 +7,7 @@ services: # container_name: modulector_dev_db # restart: always # ports: - # - 5433:5432 + # - 5432:5432 # command: postgres -c 'config_file=/etc/postgresql/postgresql.conf' # environment: # # IMPORTANT: these three params must be the same than POSTGRES_USERNAME, POSTGRES_PASSWORD, POSTGRES_DB diff --git a/modulector/migrations/0040_alter_mirnaxgene_gene_alter_mirnaxgene_score.py b/modulector/migrations/0040_alter_mirnaxgene_gene_alter_mirnaxgene_score.py new file mode 100644 index 0000000..1d5499c --- /dev/null +++ b/modulector/migrations/0040_alter_mirnaxgene_gene_alter_mirnaxgene_score.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.2 on 2023-10-28 11:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('modulector', '0039_alter_methylationepic_id'), + ] + + operations = [ + migrations.AlterField( + model_name='mirnaxgene', + name='gene', + field=models.CharField(db_index=True, max_length=50), + ), + migrations.AlterField( + model_name='mirnaxgene', + name='score', + field=models.DecimalField(db_index=True, decimal_places=4, max_digits=20), + ), + ] diff --git a/modulector/models.py b/modulector/models.py index 6043d62..ad82425 100644 --- a/modulector/models.py +++ b/modulector/models.py @@ -14,7 +14,8 @@ class DatasetSeparator(models.TextChoices): # Modelos para Methylation class MethylationEPIC(models.Model): - id = models.IntegerField(primary_key=True, blank=False, null=False, db_index=True) + id = models.IntegerField( + primary_key=True, blank=False, null=False, db_index=True) ilmnid = models.CharField( max_length=25, blank=False, null=False, db_index=True) name = models.CharField(max_length=15, blank=False, @@ -107,9 +108,9 @@ class Meta: class MirnaXGene(models.Model): - mirna = models.ForeignKey(Mirna, on_delete=models.CASCADE) - gene = models.CharField(max_length=50) - score = models.DecimalField(max_digits=20, decimal_places=4) + mirna = models.ForeignKey(Mirna, on_delete=models.CASCADE, db_index=True) + gene = models.CharField(max_length=50, db_index=True) + score = models.DecimalField(max_digits=20, decimal_places=4, db_index=True) mirna_source = models.ForeignKey(MirnaSource, on_delete=models.CASCADE) sources = models.CharField(max_length=500, null=True) score_class = models.CharField(max_length=3, null=True) diff --git a/modulector/templates/index.html b/modulector/templates/index.html index aaf3507..caa5cae 100644 --- a/modulector/templates/index.html +++ b/modulector/templates/index.html @@ -13,9 +13,6 @@

    Modulector v{{ version }}

  • Mirna Target Interactions
  • -
  • - Mirna Interactions -
  • Mirna Aliases
  • diff --git a/modulector/views.py b/modulector/views.py index d6896c9..4685245 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -59,58 +59,58 @@ def get_limit_parameter(value: Optional[str]) -> int: return DEFAULT_PAGE_SIZE -class MirnaTargetInteractions(viewsets.ReadOnlyModelViewSet): - """Returns a single instance with data about an interaction between a miRNA and a gene - (mirna-target-interactions endpoint)""" - serializer_class = MirnaXGenSerializer - handler400 = 'rest_framework.exceptions.bad_request' - - @staticmethod - def __get_gene_aliases(gene: str) -> List[str]: - """Retrieves the aliases for a gene based on the gene provided""" - match_gene = GeneAliases.objects.filter( - Q(alias=gene) | Q(gene_symbol=gene)).first() - if match_gene is None: - return [] - - gene_symbol = match_gene.gene_symbol - aliases = list(GeneAliases.objects.filter( - gene_symbol=gene_symbol).values_list('alias', flat=True).distinct()) - # Adds the parameter to not omit it in the future search - aliases.append(gene) - return aliases - - def list(self, request, *args, **kwargs): - mirna = self.request.GET.get("mirna") - gene = self.request.GET.get("gene") - - if not mirna or not gene: - raise ParseError(detail="mirna and gene are obligatory") - - # Gets gene aliases - gene_aliases = self.__get_gene_aliases(gene) - - # Gets miRNA aliases - mirna_aliases = get_mirna_aliases(mirna) - instance = generics.get_object_or_404( - MirnaXGene, mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) - serializer = self.get_serializer(instance) - return Response(serializer.data) - - -class MirnaInteractions(generics.ListAPIView): +# class MirnaTargetInteractions(viewsets.ReadOnlyModelViewSet): +# """Returns a single instance with data about an interaction between a miRNA and a gene +# (mirna-target-interactions endpoint)""" +# serializer_class = MirnaXGenSerializer +# handler400 = 'rest_framework.exceptions.bad_request' + +# @staticmethod +# def __get_gene_aliases(gene: str) -> List[str]: +# """Retrieves the aliases for a gene based on the gene provided""" +# match_gene = GeneAliases.objects.filter( +# Q(alias=gene) | Q(gene_symbol=gene)).first() +# if match_gene is None: +# return [] + +# gene_symbol = match_gene.gene_symbol +# aliases = list(GeneAliases.objects.filter( +# gene_symbol=gene_symbol).values_list('alias', flat=True).distinct()) +# # Adds the parameter to not omit it in the future search +# aliases.append(gene) +# return aliases + +# def list(self, request, *args, **kwargs): +# mirna = self.request.GET.get("mirna") +# gene = self.request.GET.get("gene") + +# if not mirna or not gene: +# raise ParseError(detail="mirna and gene are obligatory") + +# # Gets gene aliases +# gene_aliases = self.__get_gene_aliases(gene) + +# # Gets miRNA aliases +# mirna_aliases = get_mirna_aliases(mirna) +# instance = generics.get_object_or_404( +# MirnaXGene, mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) +# serializer = self.get_serializer(instance) +# return Response(serializer.data) + + +class MirnaTargetInteractions(generics.ListAPIView): """Returns a paginated response with all the interactions of a specific miRNA (mirna-interactions endpoint)""" serializer_class = MirnaXGenSerializer pagination_class = StandardResultsSetPagination - filter_backends = [filters.OrderingFilter, filters.SearchFilter] + filter_backends = [filters.OrderingFilter] ordering_fields = ['gene', 'score'] ordering = ['id'] - search_fields = ['gene'] handler400 = 'rest_framework.exceptions.bad_request' @staticmethod def __get_gene_aliases(gene: str) -> List[str]: """Retrieves the aliases for a gene based on the gene provided""" + print() match_gene = GeneAliases.objects.filter( Q(alias=gene) | Q(gene_symbol=gene)).first() if match_gene is None: From b699b74832e59a4dee12e3ddddb530bd4b2d221b Mon Sep 17 00:00:00 2001 From: mauricio Date: Wed, 1 Nov 2023 12:39:20 -0300 Subject: [PATCH 10/11] include_pubmeds param in mirna-target-interactions --- README.md | 12 +++-------- modulector/serializers.py | 23 +++++++++++++------- modulector/views.py | 45 ++++++--------------------------------- 3 files changed, 24 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 0294e11..57459fd 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,7 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is - `mirna`: miRNA (Accession ID or name in mirBase) to get its interactions with different genes targets. - `gene`: gene symbol to get its interactions with different miRNAs targets. - `score`: numerical score to filter the interactions (only interactions with a score greater than or equal to the parameter value are returned). The value of this score is provided by the mirDip database. + - `include_pubmeds`: if its value is 'true', the endpoint also returns a list of links to Pubmed where the mirnas are related to the genes (this may affect Modulector's response time). Default is 'false'. *NOTE*: mirna or gene are required - Functions: - Ordering fields: `gene` and `score` @@ -119,7 +120,7 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is - `sources`: miRNA-Gene interaction sources which publish this interaction. mirDIP score is based on the scores of those sources. This field is an array that contains the interaction score source names. - `score_class`: `L` (Low), `M` (Medium), `H` (High) or `V` (Very high) - Example: - - URL: http://localhost:8000/mirna-target-interactions?mirna=hsa-miR-891a-5p&gene=EGFR + - URL: http://localhost:8000/mirna-target-interactions?mirna=hsa-miR-891a-5p&gene=EGFR&include_pubmeds=true - Response: ```json { @@ -139,14 +140,7 @@ If no gene symbol is entered, all mirna interactions are returned. If a mirna is "https://pubmed.ncbi.nlm.nih.gov/8948606", "https://pubmed.ncbi.nlm.nih.gov/5642539", "https://pubmed.ncbi.nlm.nih.gov/9361765", - "https://pubmed.ncbi.nlm.nih.gov/4895700", - "https://pubmed.ncbi.nlm.nih.gov/5877664", - "https://pubmed.ncbi.nlm.nih.gov/9421182", - "https://pubmed.ncbi.nlm.nih.gov/6998431", - "https://pubmed.ncbi.nlm.nih.gov/7053609", - "https://pubmed.ncbi.nlm.nih.gov/8544338", - "https://pubmed.ncbi.nlm.nih.gov/9253007", - "https://pubmed.ncbi.nlm.nih.gov/10008540" + "https://pubmed.ncbi.nlm.nih.gov/4895700" ], "sources":[ "MirAncesTar", diff --git a/modulector/serializers.py b/modulector/serializers.py index 10f1c0f..ff84f7f 100644 --- a/modulector/serializers.py +++ b/modulector/serializers.py @@ -13,7 +13,8 @@ class MirnaColumnsSerializer(serializers.ModelSerializer): class Meta: model = MirnaColumns - fields = ['id', 'position', 'column_name', 'field_to_map', 'mirna_source_id'] + fields = ['id', 'position', 'column_name', + 'field_to_map', 'mirna_source_id'] class MirnaSourceSerializer(serializers.ModelSerializer): @@ -37,25 +38,30 @@ def create(self, validated_data): class MirnaXGenSerializer(serializers.ModelSerializer): - source_name = serializers.CharField(read_only=True, source='mirna_source.name') + source_name = serializers.CharField( + read_only=True, source='mirna_source.name') mirna = serializers.CharField(read_only=True, source='mirna.mirna_code') - score = serializers.FloatField(read_only=True) pubmeds = serializers.SerializerMethodField(method_name='get_pubmeds') sources = serializers.SerializerMethodField(method_name='get_sources') class Meta: model = MirnaXGene - fields = ['id', 'mirna', 'gene', 'score', 'source_name', 'pubmeds', 'sources', 'score_class'] + fields = ['id', 'mirna', 'gene', 'score', + 'source_name', 'pubmeds', 'sources', 'score_class'] - @staticmethod - def get_pubmeds(mirna_gene_interaction: MirnaXGene) -> Set[str]: + def get_pubmeds(self, mirna_gene_interaction: MirnaXGene) -> Set[str]: """ Gets a list of related Pubmed URLs to a miRNA-Gene interaction :param mirna_gene_interaction: miRNA-Gene interaction :return: List of Pubmed URLs """ pubmed_urls: Set[str] = set() - pubmed_urls.update(list(mirna_gene_interaction.pubmed.values_list('pubmed_url', flat=True))) + + if not self.context["include_pubmeds"]: + return pubmed_urls + + pubmed_urls.update( + list(mirna_gene_interaction.pubmed.values_list('pubmed_url', flat=True))) mirna = mirna_gene_interaction.mirna.mirna_code gene = mirna_gene_interaction.gene term = pubmed_service.build_search_term(mirna, gene) @@ -94,7 +100,8 @@ class Meta: class MirnaSerializer(serializers.ModelSerializer): - mirbase_accession_id = serializers.CharField(read_only=True, source='mirbase_accession_id.mirbase_accession_id') + mirbase_accession_id = serializers.CharField( + read_only=True, source='mirbase_accession_id.mirbase_accession_id') links = serializers.SerializerMethodField(method_name='get_links') aliases = serializers.SerializerMethodField(method_name='get_aliases') diff --git a/modulector/views.py b/modulector/views.py index 4685245..80b3f90 100644 --- a/modulector/views.py +++ b/modulector/views.py @@ -59,45 +59,6 @@ def get_limit_parameter(value: Optional[str]) -> int: return DEFAULT_PAGE_SIZE -# class MirnaTargetInteractions(viewsets.ReadOnlyModelViewSet): -# """Returns a single instance with data about an interaction between a miRNA and a gene -# (mirna-target-interactions endpoint)""" -# serializer_class = MirnaXGenSerializer -# handler400 = 'rest_framework.exceptions.bad_request' - -# @staticmethod -# def __get_gene_aliases(gene: str) -> List[str]: -# """Retrieves the aliases for a gene based on the gene provided""" -# match_gene = GeneAliases.objects.filter( -# Q(alias=gene) | Q(gene_symbol=gene)).first() -# if match_gene is None: -# return [] - -# gene_symbol = match_gene.gene_symbol -# aliases = list(GeneAliases.objects.filter( -# gene_symbol=gene_symbol).values_list('alias', flat=True).distinct()) -# # Adds the parameter to not omit it in the future search -# aliases.append(gene) -# return aliases - -# def list(self, request, *args, **kwargs): -# mirna = self.request.GET.get("mirna") -# gene = self.request.GET.get("gene") - -# if not mirna or not gene: -# raise ParseError(detail="mirna and gene are obligatory") - -# # Gets gene aliases -# gene_aliases = self.__get_gene_aliases(gene) - -# # Gets miRNA aliases -# mirna_aliases = get_mirna_aliases(mirna) -# instance = generics.get_object_or_404( -# MirnaXGene, mirna__mirna_code__in=mirna_aliases, gene__in=gene_aliases) -# serializer = self.get_serializer(instance) -# return Response(serializer.data) - - class MirnaTargetInteractions(generics.ListAPIView): """Returns a paginated response with all the interactions of a specific miRNA (mirna-interactions endpoint)""" serializer_class = MirnaXGenSerializer @@ -123,6 +84,12 @@ def __get_gene_aliases(gene: str) -> List[str]: aliases.append(gene) return aliases + def get_serializer_context(self): + include_pubmeds = self.request.GET.get("include_pubmeds") == "true" + context = super(MirnaTargetInteractions, self).get_serializer_context() + context.update({'include_pubmeds': include_pubmeds}) + return context + def get_queryset(self): mirna = self.request.GET.get("mirna") gene = self.request.GET.get("gene") From a99c7c6298e79cc9a56c16d3e270e3c4d19c1198 Mon Sep 17 00:00:00 2001 From: mauricio Date: Wed, 1 Nov 2023 12:44:01 -0300 Subject: [PATCH 11/11] change version from 2.0.3 to 2.1.0 --- ModulectorBackend/settings.py | 8 ++++---- ModulectorBackend/settings_ci.py | 8 ++++---- ModulectorBackend/settings_dev.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ModulectorBackend/settings.py b/ModulectorBackend/settings.py index 76d1e9d..e19cb40 100644 --- a/ModulectorBackend/settings.py +++ b/ModulectorBackend/settings.py @@ -13,7 +13,7 @@ import os # Modulector version -VERSION: str = '2.0.3' +VERSION: str = '2.1.0' # Default primary key field type # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field @@ -57,7 +57,8 @@ ] CRONJOBS = [ - ('0 0 * * SAT', 'modulector.pubmed_job.execute', '>> ' + BASE_DIR + '/jobs_log.log') + ('0 0 * * SAT', 'modulector.pubmed_job.execute', + '>> ' + BASE_DIR + '/jobs_log.log') ] CRONTAB_LOCK_JOBS = True ALLOW_PARALLEL_RUNS = False @@ -84,8 +85,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , + 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/ModulectorBackend/settings_ci.py b/ModulectorBackend/settings_ci.py index 1fdd158..af51c28 100644 --- a/ModulectorBackend/settings_ci.py +++ b/ModulectorBackend/settings_ci.py @@ -13,7 +13,7 @@ import os # Modulector version -VERSION: str = '2.0.3' +VERSION: str = '2.1.0' # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -53,7 +53,8 @@ ] CRONJOBS = [ - ('0 0 * * SAT', 'modulector.pubmed_job.execute', '>> ' + BASE_DIR + '/jobs_log.log') + ('0 0 * * SAT', 'modulector.pubmed_job.execute', + '>> ' + BASE_DIR + '/jobs_log.log') ] CRONTAB_LOCK_JOBS = True ALLOW_PARALLEL_RUNS = False @@ -80,8 +81,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , + 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ diff --git a/ModulectorBackend/settings_dev.py b/ModulectorBackend/settings_dev.py index 21b519d..aa8188b 100644 --- a/ModulectorBackend/settings_dev.py +++ b/ModulectorBackend/settings_dev.py @@ -13,7 +13,7 @@ import os # Modulector version -VERSION: str = '2.0.3' +VERSION: str = '2.1.0' # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -53,7 +53,8 @@ ] CRONJOBS = [ - ('0 0 * * SAT', 'modulector.pubmed_job.execute', '>> ' + BASE_DIR + '/jobs_log.log') + ('0 0 * * SAT', 'modulector.pubmed_job.execute', + '>> ' + BASE_DIR + '/jobs_log.log') ] CRONTAB_LOCK_JOBS = True ALLOW_PARALLEL_RUNS = False @@ -80,8 +81,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')] - , + 'DIRS': [os.path.join(BASE_DIR, 'templates')], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [