• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

HicServices / RDMP / 9974104319

17 Jul 2024 12:22PM UTC coverage: 57.308% (+0.6%) from 56.677%
9974104319

Pull #1888

github

web-flow
Merge branch 'develop' into bugfix-pk-ei-issue
Pull Request #1888: Bugfix: Fix overeager PK setting for extraction Identifiers

11072 of 20786 branches covered (53.27%)

Branch coverage included in aggregate %.

3 of 3 new or added lines in 1 file covered. (100.0%)

39 existing lines in 7 files now uncovered.

31313 of 53174 relevant lines covered (58.89%)

7930.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.61
/Rdmp.Core/CommandLine/DatabaseCreation/ExampleDatasetsCreation.cs
1
// Copyright (c) The University of Dundee 2018-2019
2
// This file is part of the Research Data Management Platform (RDMP).
3
// RDMP is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
4
// RDMP is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
5
// You should have received a copy of the GNU General Public License along with RDMP. If not, see <https://www.gnu.org/licenses/>.
6

7
using System;
8
using System.Data;
9
using System.IO;
10
using System.Linq;
11
using System.Text.RegularExpressions;
12
using SynthEHR;
13
using SynthEHR.Datasets;
14
using FAnsi;
15
using FAnsi.Discovery;
16
using FAnsi.Discovery.ConnectionStringDefaults;
17
using Microsoft.Data.SqlClient;
18
using Rdmp.Core.CohortCommitting.Pipeline;
19
using Rdmp.Core.CohortCommitting.Pipeline.Sources;
20
using Rdmp.Core.CommandExecution;
21
using Rdmp.Core.CommandExecution.AtomicCommands;
22
using Rdmp.Core.CommandLine.Options;
23
using Rdmp.Core.CommandLine.Runners;
24
using Rdmp.Core.Curation;
25
using Rdmp.Core.Curation.Data;
26
using Rdmp.Core.Curation.Data.Aggregation;
27
using Rdmp.Core.Curation.Data.Cohort;
28
using Rdmp.Core.Curation.Data.Pipelines;
29
using Rdmp.Core.Curation.FilterImporting;
30
using Rdmp.Core.Curation.FilterImporting.Construction;
31
using Rdmp.Core.DataExport.Data;
32
using Rdmp.Core.DataExport.DataExtraction.Pipeline.Destinations;
33
using Rdmp.Core.DataExport.DataRelease.Pipeline;
34
using Rdmp.Core.DataFlowPipeline;
35
using Rdmp.Core.Repositories;
36
using Rdmp.Core.ReusableLibraryCode.Checks;
37
using Rdmp.Core.ReusableLibraryCode.Progress;
38
using TypeGuesser;
39

40
namespace Rdmp.Core.CommandLine.DatabaseCreation;
41

42
/// <summary>
43
/// Handles the creation of example RDMP datasets and metadata object (catalogues, cohorts , projects etc).
44
/// </summary>
45
public partial class ExampleDatasetsCreation
46
{
47
    private readonly IRDMPPlatformRepositoryServiceLocator _repos;
48
    private readonly IBasicActivateItems _activator;
49
    public const int NumberOfPeople = 5000;
50
    public const int NumberOfRowsPerDataset = 10000;
51

52
    public ExampleDatasetsCreation(IBasicActivateItems activator, IRDMPPlatformRepositoryServiceLocator repos)
2✔
53
    {
54
        _repos = repos;
2✔
55
        _activator = activator;
2✔
56
    }
2✔
57

58
    internal void Create(DiscoveredDatabase db, ICheckNotifier notifier, PlatformDatabaseCreationOptions options)
59
    {
60
        if (db.Exists())
2✔
61
            if (options.DropDatabases)
2!
62
                db.Drop();
2✔
63
            else
64
                throw new Exception(
×
65
                    $"Database {db.GetRuntimeName()} already exists and allowDrop option was not specified");
×
66

67
        if (db.Server.Builder is SqlConnectionStringBuilder b)
2✔
68
        {
69
            var keywords = _repos.CatalogueRepository
2✔
70
                .GetAllObjects<ConnectionStringKeyword>()
2✔
71
                .Where(k => k.DatabaseType == DatabaseType.MicrosoftSQLServer)
8✔
72
                .ToArray();
2✔
73

74
            AddKeywordIfSpecified(b.TrustServerCertificate, nameof(b.TrustServerCertificate), keywords);
2✔
75
        }
76

77
        notifier.OnCheckPerformed(new CheckEventArgs($"About to create {db.GetRuntimeName()}", CheckResult.Success));
2✔
78
        //create a new database for the datasets
79
        db.Create();
2✔
80

81
        notifier.OnCheckPerformed(
2✔
82
            new CheckEventArgs($"Successfully created {db.GetRuntimeName()}", CheckResult.Success));
2✔
83

84
        //fixed seed so everyone gets the same datasets
85
        var r = new Random(options.Seed);
2✔
86

87
        notifier.OnCheckPerformed(new CheckEventArgs("Generating people", CheckResult.Success));
2✔
88
        //people
89
        var people = new PersonCollection();
2✔
90
        people.GeneratePeople(options.NumberOfPeople, r);
2✔
91

92
        //datasets
93
        var biochem = ImportCatalogue(Create<Biochemistry>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
94
            "chi", "Healthboard", "SampleDate", "TestCode"));
2✔
95
        var demography = ImportCatalogue(Create<Demography>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
96
            "chi", "dtCreated", "hb_extract"));
2✔
97
        var prescribing = ImportCatalogue(Create<Prescribing>(db, people, r, notifier, options.NumberOfRowsPerDataset,
2✔
98
            "chi", "PrescribedDate", "Name")); //<- this is slooo!
2✔
99
        var admissions = ImportCatalogue(Create<HospitalAdmissions>(db, people, r, notifier,
2✔
100
            options.NumberOfRowsPerDataset, "chi", "AdmissionDate"));
2✔
101

102
        //Create but do not import the CarotidArteryScan dataset so that users can test out referencing a brand new table
103
        Create<CarotidArteryScan>(db, people, r, notifier, options.NumberOfRowsPerDataset, "RECORD_NUMBER");
2✔
104

105
        //the following should not be extractable
106
        ForExtractionInformations(demography,
2✔
107
            e => e.DeleteInDatabase(),
42✔
108
            "chi_num_of_curr_record",
2✔
109
            "surname",
2✔
110
            "forename",
2✔
111
            "current_address_L1",
2✔
112
            "current_address_L2",
2✔
113
            "current_address_L3",
2✔
114
            "current_address_L4",
2✔
115
            "birth_surname",
2✔
116
            "previous_surname",
2✔
117
            "midname",
2✔
118
            "alt_forename",
2✔
119
            "other_initials",
2✔
120
            "previous_address_L1",
2✔
121
            "previous_address_L2",
2✔
122
            "previous_address_L3",
2✔
123
            "previous_address_L4",
2✔
124
            "previous_postcode",
2✔
125
            "date_address_changed",
2✔
126
            "adr",
2✔
127
            "previous_gp_accept_date",
2✔
128
            "hic_dataLoadRunID");
2✔
129

130
        //the following should be special approval only
131
        ForExtractionInformations(demography,
2✔
132
            e =>
2✔
133
            {
2✔
134
                e.ExtractionCategory = ExtractionCategory.SpecialApprovalRequired;
8✔
135
                e.SaveToDatabase();
8✔
136
            },
8✔
137
            "current_postcode",
2✔
138
            "current_gp",
2✔
139
            "previous_gp",
2✔
140
            "date_of_birth");
2✔
141

142

143
        CreateAdmissionsViews(db);
2✔
144
        var vConditions = ImportCatalogue(db.ExpectTable("vConditions", null, TableType.View));
2✔
145
        var vOperations = ImportCatalogue(db.ExpectTable("vOperations", null, TableType.View));
2✔
146

147
        CreateGraph(biochem, "Test Codes", "TestCode", false, null);
2✔
148
        CreateGraph(biochem, "Test Codes By Date", "SampleDate", true, "TestCode");
2✔
149

150
        CreateFilter(biochem, "Creatinine", "TestCode", "TestCode like '%CRE%'",
2✔
151
            @"Serum creatinine is a blood measurement.  It is an indicator of renal health.");
2✔
152
        CreateFilter(biochem, "Test Code", "TestCode", "TestCode like @code", "Filters any test code set");
2✔
153

154
        CreateExtractionInformation(demography, "Age", "date_of_birth",
2✔
155
            "FLOOR(DATEDIFF(DAY, date_of_birth, GETDATE()) / 365.25) As Age");
2✔
156
        var fAge = CreateFilter(demography, "Older at least x years", "Age",
2✔
157
            "FLOOR(DATEDIFF(DAY, date_of_birth, GETDATE()) / 365.25) >= @age",
2✔
158
            "Patients age is greater than or equal to the provided @age");
2✔
159
        SetParameter(fAge, "@age", "int", "16");
2✔
160

161
        CreateGraph(demography, "Patient Ages", "Age", false, null);
2✔
162

163
        CreateGraph(prescribing, "Approved Name", "ApprovedName", false, null);
2✔
164
        CreateGraph(prescribing, "Approved Name Over Time", "PrescribedDate", true, "ApprovedName");
2✔
165

166
        CreateGraph(prescribing, "Bnf", "FormattedBnfCode", false, null);
2✔
167
        CreateGraph(prescribing, "Bnf Over Time", "PrescribedDate", true, "FormattedBnfCode");
2✔
168

169
        CreateFilter(
2✔
170
            CreateGraph(vConditions, "Conditions frequency", "Field", false, "Condition"),
2✔
171
            "Common Conditions Only",
2✔
172
            @"(Condition in 
2✔
173
(select top 40 Condition from vConditions c
2✔
174
 WHERE Condition <> 'NULL' AND Condition <> 'Nul' 
2✔
175
 group by Condition order by count(*) desc))");
2✔
176

177
        CreateFilter(
2✔
178
            CreateGraph(vOperations, "Operation frequency", "Field", false, "Operation"),
2✔
179
            "Common Operation Only",
2✔
180
            @"(Operation in 
2✔
181
(select top 40 Operation from vOperations c
2✔
182
 WHERE Operation <> 'NULL' AND Operation <> 'Nul' 
2✔
183
 group by Operation order by count(*) desc))");
2✔
184

185
        //group these all into the same folder
186
        admissions.Folder = @"\admissions";
2✔
187
        admissions.SaveToDatabase();
2✔
188
        vConditions.Folder = @"\admissions";
2✔
189
        vConditions.SaveToDatabase();
2✔
190
        vOperations.Folder = @"\admissions";
2✔
191
        vOperations.SaveToDatabase();
2✔
192

193

194
        var cmdCreateCohortTable = new ExecuteCommandCreateNewCohortStore(_activator, db, false, "chi", "varchar(10)");
2✔
195
        cmdCreateCohortTable.Execute();
2✔
196
        var externalCohortTable = cmdCreateCohortTable.Created;
2✔
197

198
        //Find the pipeline for committing cohorts
199
        var cohortCreationPipeline =
2!
200
            _repos.CatalogueRepository.GetAllObjects<Pipeline>().FirstOrDefault(p =>
2✔
201
                p?.Source?.Class == typeof(CohortIdentificationConfigurationSource).FullName) ??
10!
202
            throw new Exception("Could not find a cohort committing pipeline");
2✔
203

204
        //A cohort creation query
205
        var f = CreateFilter(vConditions, "Lung Cancer Condition", "Condition", "Condition like 'C349'",
2✔
206
            "ICD-10-CM Diagnosis Code C34.9 Malignant neoplasm of unspecified part of bronchus or lung");
2✔
207

208
        var cic = CreateCohortIdentificationConfiguration((ExtractionFilter)f);
2✔
209

210
        var cohort = CommitCohortToNewProject(cic, externalCohortTable, cohortCreationPipeline, "Lung Cancer Project",
2✔
211
            "P1 Lung Cancer Patients", 123, out var project);
2✔
212

213
        var cohortTable = cohort.ExternalCohortTable.DiscoverCohortTable();
2✔
214
        using (var con = cohortTable.Database.Server.GetConnection())
2✔
215
        {
216
            con.Open();
2✔
217
            //delete half the records (so we can simulate cohort refresh)
218
            using var cmd = cohortTable.Database.Server.GetCommand(
2✔
219
                $"DELETE TOP (10) PERCENT from {cohortTable.GetFullyQualifiedName()}", con);
2✔
220
            cmd.ExecuteNonQuery();
2✔
221
        }
222

223
        var ec1 = CreateExtractionConfiguration(project, cohort, "First Extraction (2016 - project 123)", true,
2✔
224
            notifier, biochem, prescribing, demography);
2✔
225
        var ec2 = CreateExtractionConfiguration(project, cohort, "Project 123 - 2017 Refresh", true, notifier, biochem,
2✔
226
            prescribing, demography, admissions);
2✔
227
        var ec3 = CreateExtractionConfiguration(project, cohort, "Project 123 - 2018 Refresh", true, notifier, biochem,
2✔
228
            prescribing, demography, admissions);
2✔
229

230
        ReleaseAllConfigurations(notifier, ec1, ec2, ec3);
2✔
231

232

233
        if (options.Nightmare)
2!
234
        {
235
            var nightmare = new NightmareDatasets(_repos, db)
×
236
            {
×
237
                Factor = options.NightmareFactor
×
238
            };
×
239
            nightmare.Create(externalCohortTable);
×
240
        }
241
    }
2✔
242

243
    private void AddKeywordIfSpecified(bool isEnabled, string name, ConnectionStringKeyword[] alreadyDeclared)
244
    {
245
        if (isEnabled && !alreadyDeclared.Any(k => k.Name.Equals(name)))
2✔
246
        {
247
            var keyword = new ConnectionStringKeyword(_activator.RepositoryLocator.CatalogueRepository,
2✔
248
                DatabaseType.MicrosoftSQLServer, name, "true");
2✔
249

250
            //pass it into the system wide static keyword collection for use with all databases of this type all the time
251
            DiscoveredServerHelper.AddConnectionStringKeyword(keyword.DatabaseType, keyword.Name, keyword.Value,
2✔
252
                ConnectionStringKeywordPriority.SystemDefaultMedium);
2✔
253
        }
254
    }
2✔
255

256
    private void ReleaseAllConfigurations(ICheckNotifier notifier,
257
        params ExtractionConfiguration[] extractionConfigurations)
258
    {
259
        var releasePipeline = _repos.CatalogueRepository.GetAllObjects<Pipeline>()
2✔
260
            .FirstOrDefault(p => p?.Destination?.Class == typeof(BasicDataReleaseDestination).FullName);
20!
261

262
        try
263
        {
264
            //cleanup any old releases
265
            var project = extractionConfigurations.Select(ec => ec.Project).Distinct().Single();
8✔
266

267
            var folderProvider = new ReleaseFolderProvider();
2✔
268
            var dir = folderProvider.GetFromProjectFolder(project);
2✔
269
            if (dir.Exists)
2!
270
                dir.Delete(true);
×
271
        }
2✔
272
        catch (Exception ex)
×
273
        {
274
            notifier.OnCheckPerformed(new CheckEventArgs("Could not detect/delete release folder for extractions",
×
275
                CheckResult.Warning, ex));
×
276
            return;
×
277
        }
278

279

280
        if (releasePipeline != null)
2✔
281
            try
282
            {
283
                var optsRelease = new ReleaseOptions
2✔
284
                {
2✔
285
                    Configurations = string.Join(",",
2✔
286
                        extractionConfigurations.Select(ec => ec.ID.ToString()).Distinct().ToArray()),
6✔
287
                    Pipeline = releasePipeline.ID.ToString()
2✔
288
                };
2✔
289

290
                var runnerRelease = new ReleaseRunner(optsRelease);
2✔
291
                runnerRelease.Run(_repos, ThrowImmediatelyDataLoadEventListener.Quiet, notifier,
2✔
292
                    new GracefulCancellationToken());
2✔
293
            }
2✔
UNCOV
294
            catch (Exception ex)
×
295
            {
UNCOV
296
                notifier.OnCheckPerformed(new CheckEventArgs("Could not Release ExtractionConfiguration (never mind)",
×
UNCOV
297
                    CheckResult.Warning, ex));
×
UNCOV
298
            }
×
299
    }
2✔
300

301
    private static void ForExtractionInformations(ICatalogue catalogue, Action<ExtractionInformation> action,
302
        params string[] extractionInformations)
303
    {
304
        foreach (var e in extractionInformations.Select(s => GetExtractionInformation(catalogue, s)))
158✔
305
            action(e);
50✔
306
    }
4✔
307

308
    private static void SetParameter(IFilter filter, string paramterToSet, string dataType, string value)
309
    {
310
        var p = filter.GetAllParameters().Single(fp => fp.ParameterName == paramterToSet);
4✔
311
        p.ParameterSQL = $"DECLARE {paramterToSet} AS {dataType}";
2✔
312
        p.Value = value;
2✔
313
        p.SaveToDatabase();
2✔
314
    }
2✔
315

316
    private ExtractionInformation CreateExtractionInformation(ICatalogue catalogue, string name, string columnInfoName,
317
        string selectSQL)
318
    {
319
        var col =
2!
320
            catalogue.GetTableInfoList(false).SelectMany(t => t.ColumnInfos)
2✔
321
                .SingleOrDefault(c => c.GetRuntimeName() == columnInfoName) ??
78✔
322
            throw new Exception($"Could not find ColumnInfo called '{columnInfoName}' in Catalogue {catalogue}");
2✔
323
        var ci = new CatalogueItem(_repos.CatalogueRepository, catalogue, name)
2✔
324
        {
2✔
325
            ColumnInfo_ID = col.ID
2✔
326
        };
2✔
327
        ci.SaveToDatabase();
2✔
328

329
        return new ExtractionInformation(_repos.CatalogueRepository, ci, col, selectSQL);
2✔
330
    }
331

332
    private ExtractionConfiguration CreateExtractionConfiguration(Project project, ExtractableCohort cohort,
333
        string name, bool isReleased, ICheckNotifier notifier, params ICatalogue[] catalogues)
334
    {
335
        var extractionConfiguration = new ExtractionConfiguration(_repos.DataExportRepository, project)
6✔
336
        {
6✔
337
            Name = name,
6✔
338
            Cohort_ID = cohort.ID
6✔
339
        };
6✔
340
        extractionConfiguration.SaveToDatabase();
6✔
341

342
        foreach (var c in catalogues)
56✔
343
        {
344
            //Get its extractableness
345
            var eds = _repos.DataExportRepository.GetAllObjectsWithParent<ExtractableDataSet>(c).SingleOrDefault()
22!
346
                      ?? new ExtractableDataSet(_repos.DataExportRepository, c); //or make it extractable
22✔
347

348
            extractionConfiguration.AddDatasetToConfiguration(eds);
22✔
349
        }
350

351
        var extractionPipeline = _repos.CatalogueRepository.GetAllObjects<Pipeline>().FirstOrDefault(p =>
6✔
352
            p?.Destination?.Class == typeof(ExecuteDatasetExtractionFlatFileDestination).FullName);
54!
353

354
        if (isReleased && extractionConfiguration != null)
6✔
355
        {
356
            var optsExtract = new ExtractionOptions
6✔
357
            {
6✔
358
                Pipeline = extractionPipeline.ID.ToString(),
6✔
359
                ExtractionConfiguration = extractionConfiguration.ID.ToString()
6✔
360
            };
6✔
361
            var runnerExtract = new ExtractionRunner(_activator, optsExtract);
6✔
362
            try
363
            {
364
                runnerExtract.Run(_repos, ThrowImmediatelyDataLoadEventListener.Quiet, notifier,
6✔
365
                    new GracefulCancellationToken());
6✔
366
            }
6✔
367
            catch (Exception ex)
×
368
            {
369
                notifier.OnCheckPerformed(new CheckEventArgs("Could not run ExtractionConfiguration (never mind)",
×
370
                    CheckResult.Warning, ex));
×
371
            }
×
372

373
            extractionConfiguration.IsReleased = true;
6✔
374
            extractionConfiguration.SaveToDatabase();
6✔
375
        }
376

377
        return extractionConfiguration;
6✔
378
    }
379

380
    private ExtractableCohort CommitCohortToNewProject(CohortIdentificationConfiguration cic,
381
        ExternalCohortTable externalCohortTable, IPipeline cohortCreationPipeline, string projectName,
382
        string cohortName, int projectNumber, out Project project)
383
    {
384
        //create a new data extraction Project
385
        project = new Project(_repos.DataExportRepository, projectName)
2✔
386
        {
2✔
387
            ProjectNumber = projectNumber,
2✔
388
            ExtractionDirectory = Path.GetTempPath()
2✔
389
        };
2✔
390
        project.SaveToDatabase();
2✔
391

392
        var request = new CohortCreationRequest(project,
2✔
393
            new CohortDefinition(null, cohortName, 1, projectNumber, externalCohortTable), _repos.DataExportRepository,
2✔
394
            ExtractableCohortAuditLogBuilder.GetDescription(cic))
2✔
395
        {
2✔
396
            CohortIdentificationConfiguration = cic
2✔
397
        };
2✔
398

399
        var engine = request.GetEngine(cohortCreationPipeline, ThrowImmediatelyDataLoadEventListener.Quiet);
2✔
400

401
        engine.ExecutePipeline(new GracefulCancellationToken());
2✔
402

403
        return request.CohortCreatedIfAny;
2✔
404
    }
405

406
    private CohortIdentificationConfiguration CreateCohortIdentificationConfiguration(ExtractionFilter inclusionFilter1)
407
    {
408
        //Create the top level configuration object
409
        var cic = new CohortIdentificationConfiguration(_repos.CatalogueRepository, "Tayside Lung Cancer Cohort");
2✔
410

411
        //create a UNION container for Inclusion Criteria
412
        var container = new CohortAggregateContainer(_repos.CatalogueRepository, SetOperation.UNION)
2✔
413
        {
2✔
414
            Name = "Inclusion Criteria"
2✔
415
        };
2✔
416
        container.SaveToDatabase();
2✔
417

418
        cic.RootCohortAggregateContainer_ID = container.ID;
2✔
419
        cic.SaveToDatabase();
2✔
420

421
        //Create a new cohort set to the 'Inclusion Criteria' based on the filters Catalogue
422
        var cata = inclusionFilter1.ExtractionInformation.CatalogueItem.Catalogue;
2✔
423
        var ac = cic.CreateNewEmptyConfigurationForCatalogue(cata,
2✔
424
            (a, b) => throw new Exception("Problem encountered with chi column(s)"), false);
2✔
425
        container.AddChild(ac, 0);
2✔
426

427
        //Add the filter to the WHERE logic of the cohort set
428
        var whereContainer = new AggregateFilterContainer(_repos.CatalogueRepository, FilterContainerOperation.OR);
2✔
429

430
        ac.Name = $"People with {inclusionFilter1.Name}";
2✔
431
        ac.RootFilterContainer_ID = whereContainer.ID;
2✔
432
        cic.EnsureNamingConvention(ac); //this will put cicx at the front and cause implicit SaveToDatabase
2✔
433

434
        var filterImporter = new FilterImporter(new AggregateFilterFactory(_repos.CatalogueRepository), null);
2✔
435
        var cloneFilter = filterImporter.ImportFilter(whereContainer, inclusionFilter1, null);
2✔
436

437
        whereContainer.AddChild(cloneFilter);
2✔
438

439
        return cic;
2✔
440
    }
441

442
    private IFilter CreateFilter(AggregateConfiguration graph, string name, string whereSql)
443
    {
444
        AggregateFilterContainer container;
445
        if (graph.RootFilterContainer_ID == null)
4!
446
        {
447
            container = new AggregateFilterContainer(_repos.CatalogueRepository, FilterContainerOperation.AND);
4✔
448
            graph.RootFilterContainer_ID = container.ID;
4✔
449
            graph.SaveToDatabase();
4✔
450
        }
451
        else
452
        {
453
            container = (AggregateFilterContainer)graph.RootFilterContainer;
×
454
        }
455

456
        var filter = new AggregateFilter(_repos.CatalogueRepository, name, container)
4✔
457
        {
4✔
458
            WhereSQL = whereSql
4✔
459
        };
4✔
460
        filter.SaveToDatabase();
4✔
461

462
        return filter;
4✔
463
    }
464

465
    private static void CreateAdmissionsViews(DiscoveredDatabase db)
466
    {
467
        using var con = db.Server.GetConnection();
2✔
468
        con.Open();
2✔
469
        using (var cmd = db.Server.GetCommand(
2✔
470
                   """
2✔
471
                   create view vConditions as
2✔
472

2✔
473
                   SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,Condition,Field
2✔
474
                   FROM
2✔
475
                   (
2✔
476
                     SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,MainCondition,OtherCondition1,OtherCondition2,OtherCondition3
2✔
477
                     FROM HospitalAdmissions
2✔
478
                   ) AS cp
2✔
479
                   UNPIVOT
2✔
480
                   (
2✔
481
                     Condition FOR Field IN (MainCondition,OtherCondition1,OtherCondition2,OtherCondition3)
2✔
482
                   ) AS up;
2✔
483
                   """, con))
2✔
484
        {
485
            cmd.ExecuteNonQuery();
2✔
486
        }
2✔
487

488

489
        using (var cmd = db.Server.GetCommand(
2✔
490
                   """
2✔
491
                   create view vOperations as
2✔
492

2✔
493
                   SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,Operation,Field
2✔
494
                   FROM
2✔
495
                   (
2✔
496
                     SELECT chi,DateOfBirth,AdmissionDate,DischargeDate,MainOperation,OtherOperation1,OtherOperation2,OtherOperation3
2✔
497
                     FROM HospitalAdmissions
2✔
498
                   ) AS cp
2✔
499
                   UNPIVOT
2✔
500
                   (
2✔
501
                     Operation FOR Field IN (MainOperation,OtherOperation1,OtherOperation2,OtherOperation3)
2✔
502
                   ) AS up;
2✔
503
                   """, con))
2✔
504
        {
505
            cmd.ExecuteNonQuery();
2✔
506
        }
2✔
507
    }
2✔
508

509
    private IFilter CreateFilter(ICatalogue cata, string name, string parentExtractionInformation, string whereSql,
510
        string desc)
511
    {
512
        var filter = new ExtractionFilter(_repos.CatalogueRepository, name,
8✔
513
            GetExtractionInformation(cata, parentExtractionInformation))
8✔
514
        {
8✔
515
            WhereSQL = whereSql,
8✔
516
            Description = desc
8✔
517
        };
8✔
518
        filter.SaveToDatabase();
8✔
519

520
        var parameterCreator = new ParameterCreator(filter.GetFilterFactory(), null, null);
8✔
521
        parameterCreator.CreateAll(filter, null);
8✔
522

523
        return filter;
8✔
524
    }
525

526
    /// <summary>
527
    /// Creates a new AggregateGraph for the given dataset (<paramref name="cata"/>)
528
    /// </summary>
529
    /// <param name="cata"></param>
530
    /// <param name="name">The name to give the graph</param>
531
    /// <param name="dimension1">The first dimension e.g. pass only one dimension to create a bar chart</param>
532
    /// <param name="isAxis">True if <paramref name="dimension1"/> should be created as a axis (creates a line chart)</param>
533
    /// <param name="dimension2">Optional second dimension to create (this will be the pivot column)</param>
534
    private AggregateConfiguration CreateGraph(ICatalogue cata, string name, string dimension1, bool isAxis,
535
        string dimension2)
536
    {
537
        var ac = new AggregateConfiguration(_repos.CatalogueRepository, cata, name)
18✔
538
        {
18✔
539
            CountSQL = "count(*) as NumberOfRecords"
18✔
540
        };
18✔
541
        ac.SaveToDatabase();
18✔
542
        ac.IsExtractable = true;
18✔
543

544
        var mainDimension = ac.AddDimension(GetExtractionInformation(cata, dimension1));
18✔
545
        var otherDimension = string.IsNullOrWhiteSpace(dimension2)
18✔
546
            ? null
18✔
547
            : ac.AddDimension(GetExtractionInformation(cata, dimension2));
18✔
548

549
        if (isAxis)
18✔
550
        {
551
            var axis = new AggregateContinuousDateAxis(_repos.CatalogueRepository, mainDimension)
6✔
552
            {
6✔
553
                StartDate = "'1970-01-01'",
6✔
554
                AxisIncrement = FAnsi.Discovery.QuerySyntax.Aggregation.AxisIncrement.Year
6✔
555
            };
6✔
556
            axis.SaveToDatabase();
6✔
557
        }
558

559
        if (otherDimension != null)
18✔
560
        {
561
            ac.PivotOnDimensionID = otherDimension.ID;
10✔
562
            ac.SaveToDatabase();
10✔
563
        }
564

565
        return ac;
18✔
566
    }
567

568
    private static ExtractionInformation GetExtractionInformation(ICatalogue cata, string name)
569
    {
570
        try
571
        {
572
            return cata.GetAllExtractionInformation(ExtractionCategory.Any).Single(ei =>
86✔
573
                ei.GetRuntimeName().Equals(name, StringComparison.CurrentCultureIgnoreCase));
2,446✔
574
        }
575
        catch
×
576
        {
577
            throw new Exception($"Could not find an ExtractionInformation called '{name}' in dataset {cata.Name}");
×
578
        }
579
    }
86✔
580

581
    private static DiscoveredTable Create<T>(DiscoveredDatabase db, PersonCollection people, Random r,
582
        ICheckNotifier notifier, int numberOfRecords, params string[] primaryKey) where T : IDataGenerator
583
    {
584
        var dataset = typeof(T).Name;
10✔
585
        notifier.OnCheckPerformed(new CheckEventArgs($"Generating {numberOfRecords} records for {dataset}",
10✔
586
            CheckResult.Success));
10✔
587

588
        //half a million biochemistry results
589
        var biochem = DataGeneratorFactory.Create(typeof(T), r);
10✔
590
        var dt = biochem.GetDataTable(people, numberOfRecords);
10✔
591

592
        //prune "nulls"
593
        foreach (DataRow dr in dt.Rows)
200,020✔
594
            for (var i = 0; i < dt.Columns.Count; i++)
6,160,000✔
595
                if (string.Equals(dr[i] as string, "NULL", StringComparison.CurrentCultureIgnoreCase))
2,980,000✔
596
                    dr[i] = DBNull.Value;
80,556✔
597

598

599
        notifier.OnCheckPerformed(new CheckEventArgs($"Uploading {dataset}", CheckResult.Success));
10✔
600
        var tbl = db.CreateTable(dataset, dt, GetExplicitColumnDefinitions<T>());
10✔
601

602
        if (primaryKey.Length != 0)
10✔
603
        {
604
            notifier.OnCheckPerformed(new CheckEventArgs($"Creating Primary Key {dataset}", CheckResult.Success));
10✔
605
            var cols = primaryKey.Select(s => tbl.DiscoverColumn(s)).ToArray();
36✔
606
            tbl.CreatePrimaryKey(5000, cols);
10✔
607
        }
608

609
        return tbl;
10✔
610
    }
611

612
    private static DatabaseColumnRequest[] GetExplicitColumnDefinitions<T>() where T : IDataGenerator
613
    {
614
        return typeof(T) == typeof(HospitalAdmissions)
10✔
615
            ? new[]
10✔
616
            {
10✔
617
                new DatabaseColumnRequest("MainOperation", new DatabaseTypeRequest(typeof(string), 4)),
10✔
618
                new DatabaseColumnRequest("MainOperationB", new DatabaseTypeRequest(typeof(string), 4)),
10✔
619
                new DatabaseColumnRequest("OtherOperation1", new DatabaseTypeRequest(typeof(string), 4)),
10✔
620
                new DatabaseColumnRequest("OtherOperation1B", new DatabaseTypeRequest(typeof(string), 4)),
10✔
621
                new DatabaseColumnRequest("OtherOperation2", new DatabaseTypeRequest(typeof(string), 4)),
10✔
622
                new DatabaseColumnRequest("OtherOperation2B", new DatabaseTypeRequest(typeof(string), 4)),
10✔
623
                new DatabaseColumnRequest("OtherOperation3", new DatabaseTypeRequest(typeof(string), 4)),
10✔
624
                new DatabaseColumnRequest("OtherOperation3B", new DatabaseTypeRequest(typeof(string), 4))
10✔
625
            }
10✔
626
            : null;
10✔
627
    }
628

629
    private ITableInfo ImportTableInfo(DiscoveredTable tbl)
630
    {
631
        var importer = new TableInfoImporter(_repos.CatalogueRepository, tbl);
12✔
632
        importer.DoImport(out var ti, out _);
12✔
633

634
        return ti;
12✔
635
    }
636

637
    private ICatalogue ImportCatalogue(DiscoveredTable tbl) => ImportCatalogue(ImportTableInfo(tbl));
12✔
638

639
    private ICatalogue ImportCatalogue(ITableInfo ti)
640
    {
641
        var forwardEngineer = new ForwardEngineerCatalogue(ti, ti.ColumnInfos);
12✔
642
        forwardEngineer.ExecuteForwardEngineering(out var cata, out _, out var eis);
12✔
643

644
        //get descriptions of the columns from SynthEHR
645
        cata.Description = Trim(Descriptions.Get(cata.Name));
12✔
646
        if (cata.Description != null)
12✔
647
        {
648
            cata.SaveToDatabase();
8✔
649

650
            foreach (var ci in cata.CatalogueItems)
340✔
651
            {
652
                var ciDescription = Trim(Descriptions.Get(cata.Name, ci.Name));
162✔
653
                if (ciDescription != null)
162✔
654
                {
655
                    ci.Description = ciDescription;
148✔
656
                    ci.SaveToDatabase();
148✔
657
                }
658
            }
659
        }
660

661
        var chi = eis.SingleOrDefault(e => e.GetRuntimeName().Equals("chi", StringComparison.CurrentCultureIgnoreCase));
198✔
662
        if (chi != null)
12✔
663
        {
664
            chi.IsExtractionIdentifier = true;
12✔
665
            chi.SaveToDatabase();
12✔
666

667
            new ExtractableDataSet(_repos.DataExportRepository, cata);
12✔
668
        }
669

670
        return cata;
12✔
671
    }
672

673
    private static string Trim(string s)
674
    {
675
        if (string.IsNullOrWhiteSpace(s))
174✔
676
            return null;
18✔
677

678
        //replace 2+ tabs and spaces with single spaces
679
        return SpaceTabs().Replace(s, " ").Trim();
156✔
680
    }
681

682
    [GeneratedRegex("[ \\t]{2,}")]
683
    private static partial Regex SpaceTabs();
684
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc