Add action for downloading and parsing well log from OSDU Wellbore DDMS.

This commit is contained in:
Kristian Bendiksen
2024-05-27 11:24:00 +02:00
parent 5be47b3d2c
commit 23d716754e
20 changed files with 1094 additions and 46 deletions

View File

@@ -194,6 +194,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicExportSummaryCalculationExpressionsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportSummaryCalculationExpressionsFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportWellLogCsvFileFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicImportWellLogOsduFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewViewForGridEnsembleFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewVfpPlotFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewCustomVfpPlotFeature.cpp

View File

@@ -120,7 +120,7 @@ void RiaOsduConnector::requestFieldsByName( const QString& server, const QString
params["limit"] = "10000";
params["query"] = "data.FieldName:" + fieldName;
auto reply = makeRequest( params, server, dataPartitionId, token );
auto reply = makeSearchRequest( params, server, dataPartitionId, token );
connect( reply,
&QNetworkReply::finished,
[this, reply]()
@@ -150,7 +150,7 @@ void RiaOsduConnector::requestWellsByFieldId( const QString& server, const QStri
params["limit"] = "10000";
params["query"] = QString( "nested(data.GeoContexts, (FieldID:\"%1\"))" ).arg( fieldId );
auto reply = makeRequest( params, server, dataPartitionId, token );
auto reply = makeSearchRequest( params, server, dataPartitionId, token );
connect( reply,
&QNetworkReply::finished,
[this, reply, fieldId]()
@@ -180,7 +180,7 @@ void RiaOsduConnector::requestWellboresByWellId( const QString& server, const QS
params["limit"] = "10000";
params["query"] = "data.WellID: \"" + wellId + "\"";
auto reply = makeRequest( params, server, dataPartitionId, token );
auto reply = makeSearchRequest( params, server, dataPartitionId, token );
connect( reply,
&QNetworkReply::finished,
[this, reply, wellId]()
@@ -221,7 +221,7 @@ void RiaOsduConnector::requestWellboreTrajectoryByWellboreId( const QString& ser
params["limit"] = "10000";
params["query"] = "data.WellboreID: \"" + wellboreId + "\"";
auto reply = makeRequest( params, server, dataPartitionId, token );
auto reply = makeSearchRequest( params, server, dataPartitionId, token );
connect( reply,
&QNetworkReply::finished,
[this, reply, wellboreId]()
@@ -239,7 +239,9 @@ void RiaOsduConnector::requestWellboreTrajectoryByWellboreId( const QString& ser
void RiaOsduConnector::requestFileDownloadByFileId( const QString& server, const QString& dataPartitionId, const QString& token, const QString& fileId )
{
RiaLogging::info( "Requesting download of file id: " + fileId );
auto reply = makeDownloadRequest( server, dataPartitionId, fileId, token );
QString url = constructFileDownloadUrl( server, fileId );
auto reply = makeDownloadRequest( url, dataPartitionId, token, CONTENT_TYPE_JSON );
connect( reply,
&QNetworkReply::finished,
[this, reply, fileId]()
@@ -266,7 +268,7 @@ QString RiaOsduConnector::constructSearchUrl( const QString& server )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QString RiaOsduConnector::constructDownloadUrl( const QString& server, const QString& fileId )
QString RiaOsduConnector::constructFileDownloadUrl( const QString& server, const QString& fileId )
{
return server + "/api/file/v2/files/" + fileId + "/downloadURL";
}
@@ -290,15 +292,23 @@ QString RiaOsduConnector::constructTokenUrl( const QString& authority )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QNetworkReply* RiaOsduConnector::makeRequest( const std::map<QString, QString>& parameters,
const QString& server,
const QString& dataPartitionId,
const QString& token )
QString RiaOsduConnector::constructWellLogDownloadUrl( const QString& server, const QString& wellLogId )
{
return server + "/api/os-wellbore-ddms/ddms/v3/welllogs/" + wellLogId + "/data";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QNetworkReply* RiaOsduConnector::makeSearchRequest( const std::map<QString, QString>& parameters,
const QString& server,
const QString& dataPartitionId,
const QString& token )
{
QNetworkRequest m_networkRequest;
m_networkRequest.setUrl( QUrl( constructSearchUrl( server ) ) );
addStandardHeader( m_networkRequest, token, dataPartitionId );
addStandardHeader( m_networkRequest, token, dataPartitionId, CONTENT_TYPE_JSON );
QJsonObject obj;
for ( auto [key, value] : parameters )
@@ -484,9 +494,12 @@ void RiaOsduConnector::saveFile( QNetworkReply* reply, const QString& fileId )
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaOsduConnector::addStandardHeader( QNetworkRequest& networkRequest, const QString& token, const QString& dataPartitionId )
void RiaOsduConnector::addStandardHeader( QNetworkRequest& networkRequest,
const QString& token,
const QString& dataPartitionId,
const QString& contentType )
{
networkRequest.setHeader( QNetworkRequest::ContentTypeHeader, "application/json" );
networkRequest.setHeader( QNetworkRequest::ContentTypeHeader, contentType );
networkRequest.setRawHeader( "Authorization", "Bearer " + token.toUtf8() );
networkRequest.setRawHeader( QByteArray( "Data-Partition-Id" ), dataPartitionId.toUtf8() );
}
@@ -495,17 +508,14 @@ void RiaOsduConnector::addStandardHeader( QNetworkRequest& networkRequest, const
///
//--------------------------------------------------------------------------------------------------
QNetworkReply*
RiaOsduConnector::makeDownloadRequest( const QString& server, const QString& dataPartitionId, const QString& id, const QString& token )
RiaOsduConnector::makeDownloadRequest( const QString& url, const QString& dataPartitionId, const QString& token, const QString& contentType )
{
QNetworkRequest m_networkRequest;
QNetworkRequest networkRequest;
networkRequest.setUrl( QUrl( url ) );
QString url = constructDownloadUrl( server, id );
addStandardHeader( networkRequest, token, dataPartitionId, contentType );
m_networkRequest.setUrl( QUrl( url ) );
addStandardHeader( m_networkRequest, token, dataPartitionId );
auto reply = m_networkAccessManager->get( m_networkRequest );
auto reply = m_networkAccessManager->get( networkRequest );
return reply;
}
@@ -635,6 +645,7 @@ std::pair<QString, QString> RiaOsduConnector::requestFileContentsById( const QSt
this,
SLOT( fileDownloadComplete( const QString&, const QString& ) ) );
connect( this, SIGNAL( fileDownloadFinished( const QString&, const QString& ) ), &loop2, SLOT( quit() ) );
requestFileDownloadByFileId( m_server, m_dataPartitionId, m_token, fileId );
loop2.exec();
@@ -650,3 +661,66 @@ std::pair<QString, QString> RiaOsduConnector::requestFileContentsById( const QSt
return { fileContent, "" };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::pair<QByteArray, QString> RiaOsduConnector::requestWellLogParquetDataById( const QString& wellLogId )
{
if ( m_token.isEmpty() )
{
// TODO: improve this..
QEventLoop loop;
connect( this, SIGNAL( tokenReady( const QString& ) ), &loop, SLOT( quit() ) );
requestToken();
loop.exec();
}
QEventLoop loop2;
connect( this,
SIGNAL( wellLogDownloadFinished( const QByteArray&, const QString& ) ),
this,
SLOT( wellLogDownloadComplete( const QByteArray&, const QString& ) ) );
connect( this, SIGNAL( wellLogDownloadFinished( const QByteArray&, const QString& ) ), &loop2, SLOT( quit() ) );
QString url = constructWellLogDownloadUrl( m_server, wellLogId );
RiaLogging::debug( "Well log URL: " + url );
requestWellLog( url, m_dataPartitionId, m_token );
loop2.exec();
return { m_wellLogContents, "" };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaOsduConnector::requestWellLog( const QString& url, const QString& dataPartitionId, const QString& token )
{
RiaLogging::info( "Requesting download of well log from: " + url );
auto reply = makeDownloadRequest( url, dataPartitionId, token, CONTENT_TYPE_PARQUET );
connect( reply,
&QNetworkReply::finished,
[this, reply, url]()
{
if ( reply->error() == QNetworkReply::NoError )
{
QByteArray contents = reply->readAll();
RiaLogging::info( QString( "Download succeeded: %1 bytes." ).arg( contents.length() ) );
emit wellLogDownloadFinished( contents, "" );
}
else
{
RiaLogging::error( "Download failed: " + url + " failed." + reply->errorString() );
}
} );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RiaOsduConnector::wellLogDownloadComplete( const QByteArray& contents, const QString& url )
{
m_wellLogContents = contents;
}

View File

@@ -59,9 +59,12 @@ public:
void requestWellboresByWellId( const QString& wellId );
void requestWellboreTrajectoryByWellboreId( const QString& wellboreId );
void requestFileDownloadByFileId( const QString& fileId );
void requestWellLog( const QString& url, const QString& dataPartitionId, const QString& token );
std::pair<QString, QString> requestFileContentsById( const QString& fileId );
std::pair<QByteArray, QString> requestWellLogParquetDataById( const QString& wellLogId );
QString wellIdForWellboreId( const QString& wellboreId ) const;
QString server() const;
@@ -81,9 +84,11 @@ public slots:
void saveFile( QNetworkReply* reply, const QString& fileId );
void accessGranted();
void fileDownloadComplete( const QString& fileId, const QString& filePath );
void wellLogDownloadComplete( const QByteArray&, const QString& url );
signals:
void fileDownloadFinished( const QString& fileId, const QString& filePath );
void wellLogDownloadFinished( const QByteArray& contents, const QString& url );
void fieldsFinished();
void wellsFinished();
void wellboresFinished( const QString& wellId );
@@ -91,12 +96,14 @@ signals:
void tokenReady( const QString& token );
private:
void addStandardHeader( QNetworkRequest& networkRequest, const QString& token, const QString& dataPartitionId );
void addStandardHeader( QNetworkRequest& networkRequest, const QString& token, const QString& dataPartitionId, const QString& contentType );
QNetworkReply*
makeRequest( const std::map<QString, QString>& parameters, const QString& server, const QString& dataPartitionId, const QString& token );
QNetworkReply* makeSearchRequest( const std::map<QString, QString>& parameters,
const QString& server,
const QString& dataPartitionId,
const QString& token );
QNetworkReply* makeDownloadRequest( const QString& server, const QString& dataPartitionId, const QString& id, const QString& token );
QNetworkReply* makeDownloadRequest( const QString& url, const QString& dataPartitionId, const QString& token, const QString& contentType );
void requestFieldsByName( const QString& server, const QString& dataPartitionId, const QString& token, const QString& fieldName );
void requestWellsByFieldId( const QString& server, const QString& dataPartitionId, const QString& token, const QString& fieldId );
@@ -109,9 +116,10 @@ private:
static QString generateRandomString( int length = 20 );
static QString constructSearchUrl( const QString& server );
static QString constructDownloadUrl( const QString& server, const QString& fileId );
static QString constructFileDownloadUrl( const QString& server, const QString& fileId );
static QString constructAuthUrl( const QString& authority );
static QString constructTokenUrl( const QString& authority );
static QString constructWellLogDownloadUrl( const QString& server, const QString& wellLogId );
QOAuth2AuthorizationCodeFlow* m_osdu;
QNetworkAccessManager* m_networkAccessManager;
@@ -128,9 +136,13 @@ private:
std::map<QString, std::vector<OsduWellbore>> m_wellbores;
std::map<QString, std::vector<OsduWellboreTrajectory>> m_wellboreTrajectories;
QString m_filePath;
QByteArray m_wellLogContents;
static inline const QString FIELD_KIND = "osdu:wks:master-data--Field:1.0.0";
static inline const QString WELL_KIND = "osdu:wks:master-data--Well:1.2.0";
static inline const QString WELLBORE_KIND = "osdu:wks:master-data--Wellbore:1.1.0";
static inline const QString WELLBORE_TRAJECTORY_KIND = "osdu:wks:work-product-component--WellboreTrajectory:1.1.0";
static inline const QString CONTENT_TYPE_JSON = "application/json";
static inline const QString CONTENT_TYPE_PARQUET = "application/x-parquet";
};

View File

@@ -0,0 +1,103 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2024- Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicImportWellLogOsduFeature.h"
#include "RiaGuiApplication.h"
#include "RiaLogging.h"
#include "RimOilField.h"
#include "RimOsduWellLog.h"
#include "RimOsduWellPath.h"
#include "RimProject.h"
#include "RimWellPathCollection.h"
#include "RiuMainWindow.h"
#include "OsduImportCommands/RiaOsduConnector.h"
#include "RiaLogging.h"
#include "RiaPreferences.h"
#include "cafSelectionManager.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicImportWellLogOsduFeature, "RicImportWellLogOsduFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicImportWellLogOsduFeature::isCommandEnabled() const
{
return caf::SelectionManager::instance()->selectedItemOfType<RimOsduWellPath>() != nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportWellLogOsduFeature::onActionTriggered( bool isChecked )
{
auto makeOsduConnector = []( auto app )
{
RiaPreferencesOsdu* osduPreferences = app->preferences()->osduPreferences();
const QString server = osduPreferences->server();
const QString dataPartitionId = osduPreferences->dataPartitionId();
const QString authority = osduPreferences->authority();
const QString scopes = osduPreferences->scopes();
const QString clientId = osduPreferences->clientId();
return std::make_unique<RiaOsduConnector>( RiuMainWindow::instance(), server, dataPartitionId, authority, scopes, clientId );
};
if ( auto wellPath = caf::SelectionManager::instance()->selectedItemOfType<RimOsduWellPath>() )
{
RiaGuiApplication* app = RiaGuiApplication::instance();
RimOilField* oilField = RimProject::current()->activeOilField();
if ( oilField == nullptr ) return;
if ( !oilField->wellPathCollection ) oilField->wellPathCollection = std::make_unique<RimWellPathCollection>();
RimOsduWellLog* osduWellLog = new RimOsduWellLog;
// TODO: get from OSDU...
osduWellLog->setWellLogId( "npequinor-dev:work-product-component--WellLog:aeb5bd8b1de14138afe9f23cacbc7fe7" );
oilField->wellPathCollection->addWellLog( osduWellLog, wellPath );
auto osduConnector = makeOsduConnector( app );
auto [wellLogData, errorMessage] = RimWellPathCollection::loadWellLogFromOsdu( osduConnector.get(), osduWellLog->wellLogId() );
if ( wellLogData.notNull() )
{
osduWellLog->setWellLogData( wellLogData.p() );
}
else
{
RiaLogging::error( "Importing OSDU well log failed: " + errorMessage );
}
osduWellLog->updateConnectedEditors();
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicImportWellLogOsduFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setIcon( QIcon( ":/LasFile16x16.png" ) );
actionToSetup->setText( "Import Well Log From OSDU" );
}

View File

@@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2024- Equinor ASA
//
// ResInsight 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.
//
// ResInsight 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 at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicImportWellLogOsduFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};