Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions .github/ISSUE_TEMPLATE/translation_template.csv
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@
"MetadataViewer.resx","$this.Text",,"Parquet Metadata Viewer","Parquet Metadata Önizleyicisi",""
"MetadataViewer.resx","closeButton.Text",,"Close","Kapat",""
"MetadataViewer.resx","loadingTab.Text",,"Loading...","Yükleniyor...",""
"QuickPeekForm.resx","$this.Text",,"Quick Peek","Hızlı Önizleme",""
"QuickPeekForm.resx","copyToClipboardToolStripMenuItem.Text",,"Copy to clipboard","Panoya kopyala",""
"QuickPeekForm.resx","saveImageToFileButton.Text",,"Save as PNG","PNG olarak Kaydet",""
"QuickPeekForm.resx","takeMeBackLinkLabel.Text",,"<<< back","<<< geri",""
"Errors.resx","CopyAsWhereTooLargeErrorMessage","Shown when the user right-click's on too many cells and selects the Copy as WHERE... option","The selected data is too large. Please select less cells.","Çok fazla veri seçili. Lütfen daha az hücre seçin.",""
"Errors.resx","CopyAsWhereTooLargeErrorTitle","Shown when the user right-click's on too many cells and selects the Copy as WHERE... option","Copy to clipboard failed","Panoya kopyalama başarısız oldu",""
"Errors.resx","CopyErrorMessageText","Shown in the messagebox for unhandled exceptions to teach users how to copy the error text","(CTRL+C to copy)","(Kopyalamak için CTRL+C)",""
Expand Down Expand Up @@ -210,6 +214,8 @@ Bu ayarı Yardim → Hakkinda sayfasında da yapabilirsiniz.",""
"Strings.resx","MainWindowOpenFolderTitleFormat","Title format string when a new folder is opened","Folder: {0}","Klasör: {0}",""
"Strings.resx","MetadataSuccessfullyExportedToFileMessageFormat","Shown when raw thrift metadata is successfully exported to a file","Metadata successfully exported to: {0}","Metadata başarıyla kaydedildi: {0}",""
"Strings.resx","MetadataSuccessfullyExportedToFileMessageTitle","Shown when raw thrift metadata is successfully exported to a file","Export complete","Kayıt başarılı",""
"Strings.resx","OpenFileNoLongerExistsTitleSuffix","We don't lock opened files anymore. So if the source file/folder disappears we will append this suffix to the main form title as a visual indicator for the user that the data source no longer exists.","DELETED","SİLİNDİ",""
"Strings.resx","OpenFileWasModifiedTitleSuffix","We don't lock opened files anymore. So if the source file/folder is modified we will append this suffix to the main form title as a visual indicator for the user that the data they are looking at is stale.","MODIFIED","DEĞİŞTİRİLDİ",""
"Strings.resx","PrivacyPolicyLabelText","Title to be shown before the privacy policy","Privacy policy","Gizlilik politikası",""
"Strings.resx","QueryFinishedStatusText","Shown in the Query Editor status bar when the query finishes executing","Finished in:","Tamamlandı:",""
"Strings.resx","QueryRunningStatusText","Shown in the Query Editor tool when the query is running","Running:","İşleniyor:",""
Expand Down Expand Up @@ -269,7 +275,4 @@ Onun yerine sonuçlari {2} dosyasına aktarmak ister misiniz?",""
"Strings.resx","TooManyFieldsErrorFormat","Shown on the Field Selection Dialog when there are too many fields to filter by","Too many fields: {0}","Desteklenmeyen sayıda alan: {0}",""
"Strings.resx","TypeText","Shown in the title bar of quick peek windows for image previews","Type","Format",""
"Strings.resx","UnsupportedFieldCountTextFormat","Shown in the field selection dialog to indicate how many fields are unsupported by ParquetViewer","Unsupported: {0}","Desteklenmeyen: {0}",""
"QuickPeekForm.resx","$this.Text",,"Quick Peek","Hızlı Önizleme",""
"QuickPeekForm.resx","copyToClipboardToolStripMenuItem.Text",,"Copy to clipboard","Panoya kopyala",""
"QuickPeekForm.resx","saveImageToFileButton.Text",,"Save as PNG","PNG olarak Kaydet",""
"QuickPeekForm.resx","takeMeBackLinkLabel.Text",,"<<< back","<<< geri",""
"Strings.resx","WordWrapContextMenuItemText","Right click context menu item shown when a column has cut off text and the user right clicks the column header.","Wrap Text","Metni Kaydır",""
2 changes: 1 addition & 1 deletion .github/workflows/build-test-publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ jobs:

- uses: ncipollo/release-action@v1
with:
artifacts: "signed-package/ParquetViewer.exe,signed-package/ParquetViewer_SelfContained.exe"
artifacts: "/signed-package/ParquetViewer.exe,/signed-package/ParquetViewer_SelfContained.exe"

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The release action keeps failing to find the signed exe's so going to give this a try for next release

body: "PR: #${{ env.PR_NUMBER }}"
allowUpdates: ${{ env.BRANCH_NAME != 'main' }}
omitBodyDuringUpdate: true
Expand Down
6 changes: 3 additions & 3 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Apache.Arrow" Version="22.1.0" />
<PackageVersion Include="Apache.Arrow" Version="23.0.0" />
<PackageVersion Include="dotnet-file-associator" Version="0.1.4" />
<PackageVersion Include="DuckDB.NET.Data.Full" Version="1.4.3" />
<PackageVersion Include="FCTB" Version="2.16.24" />
<PackageVersion Include="MiniExcel" Version="2.0.0-preview.2" />
<PackageVersion Include="MiniExcel" Version="2.0.0-preview.4" />
<PackageVersion Include="MSTest.TestAdapter" Version="4.0.2" />
<PackageVersion Include="MSTest.TestFramework" Version="4.0.2" />
<PackageVersion Include="NAudio" Version="2.3.0" />
<PackageVersion Include="NAudio.WinForms" Version="2.3.0" />
<PackageVersion Include="Parquet.Net" Version="5.6.0-pre.3" />
<PackageVersion Include="Parquet.Net" Version="5.6.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
</ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions src/ParquetViewer.Engine.DuckDB/ParquetEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -459,5 +459,7 @@ private static string MakeColumnSafe(string columnName)
public async Task WriteDataToParquetFileAsync(DataTable dataTable, string path, CancellationToken cancellationToken,
IProgress<int> progress, Dictionary<string, string>? customMetadata)
=> throw new NotImplementedException();

public IEnumerable<string> GetOpenParquetFilePaths() => this._dbs.Select(db => db.ParquetFilePath);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,11 @@ private DataTableLite BuildDataTable(ParquetSchemaElement? parent, List<string>

throw;
}
catch (ParquetException ex)
{
var maskedExMessage = ex.Message.Replace($"'{field.Path}'", $"`{field.Path}`");
throw new ParquetEngineException(maskedExMessage, ex);
}
}
}
}
40 changes: 24 additions & 16 deletions src/ParquetViewer.Engine.ParquetNET/ParquetEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ namespace ParquetViewer.Engine.ParquetNET
public partial class ParquetEngine : IParquetEngine, IDisposable
{
private static readonly ParquetOptions _defaultParquetOptions = new () { UseDateOnlyTypeForDates = true, UseTimeOnlyTypeForTimeMicros = true, UseTimeOnlyTypeForTimeMillis = true };
private readonly ParquetReader[] _parquetFiles;
private readonly (string ParquetFilePath, ParquetReader Reader)[] _parquetFiles;
private long? _recordCount;

private ParquetReader _defaultReader => _parquetFiles.FirstOrDefault() ?? throw new ParquetEngineException("No parquet readers available");
private ParquetReader _defaultReader => _parquetFiles.Length > 0 ? _parquetFiles[0].Reader : throw new ParquetEngineException("No parquet readers available");

private FileMetaData _thriftMetadata => _defaultReader.Metadata ?? throw new ParquetEngineException("No thrift metadata was found");

private ParquetSchema _schema => _defaultReader.Schema;

public Dictionary<string, string> CustomMetadata => _defaultReader.CustomMetadata;

public long RecordCount => _recordCount ??= _parquetFiles.Sum(pf => pf.Metadata?.NumRows ?? 0);
public long RecordCount => _recordCount ??= _parquetFiles.Sum(pf => pf.Reader.Metadata?.NumRows ?? 0);

public int NumberOfPartitions => _parquetFiles.Length;

Expand All @@ -32,7 +32,7 @@ public partial class ParquetEngine : IParquetEngine, IDisposable
ParquetMetadata? _metadata = null;
public IParquetMetadata Metadata => _metadata ??= new ParquetMetadata(_thriftMetadata, BuildParquetSchemaTree(), (int)RecordCount);

private ParquetEngine(string fileOrFolderPath, params ParquetReader[] parquetFiles)
private ParquetEngine(string fileOrFolderPath, params (string FilePath, ParquetReader Reader)[] parquetFiles)
{
_parquetFiles = parquetFiles ?? throw new ArgumentNullException(nameof(parquetFiles), "No parquet readers provided");
Path = fileOrFolderPath;
Expand Down Expand Up @@ -94,13 +94,16 @@ public static async Task<ParquetEngine> OpenFileAsync(string parquetFilePath, Ca
throw new FileNotFoundException($"Could not find parquet file at: {parquetFilePath}");
}

Stream? readOnlyNonLockingStream = null;
try
{
var parquetReader = await ParquetReader.CreateAsync(parquetFilePath, _defaultParquetOptions, cancellationToken);
return new ParquetEngine(parquetFilePath, parquetReader);
readOnlyNonLockingStream = new FileStream(parquetFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
var parquetReader = await ParquetReader.CreateAsync(readOnlyNonLockingStream, _defaultParquetOptions, false, cancellationToken);
return new ParquetEngine(parquetFilePath, (parquetFilePath, parquetReader));
}
catch (Exception ex)
{
readOnlyNonLockingStream?.Dispose();
throw new FileReadException(ex);
}
}
Expand All @@ -113,23 +116,26 @@ public static async Task<ParquetEngine> OpenFolderAsync(string folderPath, Cance
}

var skippedFiles = new Dictionary<string, Exception>();
var fileGroups = new Dictionary<ParquetSchema, List<ParquetReader>>();
var fileGroups = new Dictionary<ParquetSchema, List<(string FilePath, ParquetReader Reader)>>();
foreach (var file in Engine.Helpers.ListParquetFiles(folderPath))
{
cancellationToken.ThrowIfCancellationRequested();

Stream? readOnlyNonLockingStream = null;
try
{
var parquetReader = await ParquetReader.CreateAsync(file, _defaultParquetOptions, cancellationToken);
readOnlyNonLockingStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
var parquetReader = await ParquetReader.CreateAsync(readOnlyNonLockingStream, _defaultParquetOptions, false, cancellationToken);
if (!fileGroups.ContainsKey(parquetReader.Schema))
{
fileGroups.Add(parquetReader.Schema, new List<ParquetReader>());
fileGroups.Add(parquetReader.Schema, new List<(string, ParquetReader)>());
}

fileGroups[parquetReader.Schema].Add(parquetReader);
fileGroups[parquetReader.Schema].Add((file, parquetReader));
}
Comment thread
mukunku marked this conversation as resolved.
catch (Exception ex)
{
readOnlyNonLockingStream?.Dispose();
skippedFiles.Add(System.IO.Path.GetRelativePath(folderPath, file), ex);
}
Comment thread
mukunku marked this conversation as resolved.
}
Expand All @@ -150,7 +156,7 @@ public static async Task<ParquetEngine> OpenFolderAsync(string folderPath, Cance
//We found more than one type of schema.
foreach (var fileGroupList in fileGroups.Values)
{
Engine.Helpers.EZDispose(fileGroupList);
Engine.Helpers.EZDispose(fileGroupList.Select(f => f.Reader));
}

throw new MultipleSchemasFoundException(fileGroups.Keys.ToList()
Expand All @@ -159,7 +165,7 @@ public static async Task<ParquetEngine> OpenFolderAsync(string folderPath, Cance
else if (skippedFiles.Count > 0)
{
//We found one schema but some files couldn't be read
Engine.Helpers.EZDispose(fileGroups.Values.First());
Engine.Helpers.EZDispose(fileGroups.Values.First().Select(f => f.Reader));
throw new SomeFilesSkippedException(skippedFiles);
}

Expand All @@ -172,13 +178,13 @@ public static async Task<ParquetEngine> OpenFolderAsync(string folderPath, Cance
{
foreach (var parquetFile in _parquetFiles)
{
if (offset >= parquetFile.Metadata?.NumRows)
if (offset >= parquetFile.Reader.Metadata?.NumRows)
{
offset -= parquetFile.Metadata.NumRows;
offset -= parquetFile.Reader.Metadata.NumRows;
continue;
}

yield return (offset, parquetFile);
yield return (offset, parquetFile.Reader);
offset = 0;
}
}
Expand Down Expand Up @@ -230,7 +236,7 @@ public async Task WriteDataToParquetFileAsync(DataTable dataTable, string path,
}
}

public void Dispose() => Engine.Helpers.EZDispose(_parquetFiles);
public void Dispose() => Engine.Helpers.EZDispose(_parquetFiles.Select(f => f.Reader));

private static System.Type GetNullableVersion(System.Type sourceType) => sourceType == null
? throw new ArgumentNullException(nameof(sourceType))
Expand Down Expand Up @@ -279,5 +285,7 @@ private static Array GetColumnValues(DataTable dataTable, System.Type type, stri

return values;
}

public IEnumerable<string> GetOpenParquetFilePaths() => this._parquetFiles.Select(db => db.ParquetFilePath);
}
}
2 changes: 2 additions & 0 deletions src/ParquetViewer.Engine/IParquetEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ Task<Func<bool, DataTable>> ReadRowsAsync(List<string> selectedFields, int offse

Task WriteDataToParquetFileAsync(DataTable dataTable, string path, CancellationToken cancellationToken,
IProgress<int> progress, Dictionary<string, string>? customMetadata);

IEnumerable<string> GetOpenParquetFilePaths();
}
}
7 changes: 1 addition & 6 deletions src/ParquetViewer.Engine/Types/IByteArrayValue.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace ParquetViewer.Engine.Types
{
Expand Down
3 changes: 1 addition & 2 deletions src/ParquetViewer/Analytics/AllEvents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,10 @@ public enum ActionId
public class ExceptionEvent : AmplitudeEvent
{
public const string MASK_SENTINEL = "*****";

private const string EVENT_TYPE = "exception.thrown";

[JsonIgnore]
public System.Exception Exception { get; }
public Exception Exception { get; }

public string Message
{
Expand Down
35 changes: 20 additions & 15 deletions src/ParquetViewer/Controls/AudioPlayerDataGridViewCell.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,24 +265,29 @@ protected override void OnMouseUp(DataGridViewCellMouseEventArgs e)
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
base.OnMouseClick(e);
if (e.Button != MouseButtons.Left)
return;

if (ContainsCursor(this._playPauseButtonBounds, e.Location) && !this._isCellTooSmall)
{
TogglePlayPause();
}
else if (ContainsCursor(this._stopButtonBounds, e.Location) && !this._isCellTooSmall)
{
StopPlayback();
}
else if (ContainsCursor(this._contextMenuButtonBounds, e.Location) && !this._isCellTooSmall)
if (e.Button == MouseButtons.Left)
{
ShowContextMenu(e.Location);
if (ContainsCursor(this._playPauseButtonBounds, e.Location) && !this._isCellTooSmall)
{
TogglePlayPause();
}
else if (ContainsCursor(this._stopButtonBounds, e.Location) && !this._isCellTooSmall)
{
StopPlayback();
}
else if (ContainsCursor(this._contextMenuButtonBounds, e.Location) && !this._isCellTooSmall)
{
ShowContextMenu(e.Location);
}
else if (ContainsCursor(this._trackBarBounds, e.Location))
{
Seek(e.Location);
}
}
else if (ContainsCursor(this._trackBarBounds, e.Location))
else if (e.Button == MouseButtons.Right)
{
Seek(e.Location);
// Allow play/pause even when no ui buttons are rendered
TogglePlayPause();
}
}

Expand Down
Loading
Loading