Friday, August 24, 2018

Updating Embedded Array And Objects In Mongo

In Mongo I have a collection of post and each post has comments that are embed with a user id, user name and user email.

- post
    - comments
           - user_id name email

I was wondering how can I update the all the emails that are associated with a certain id?

Solved

Currently, you would have to retrieve comments first, and then update emails.

Depending on which client you use, here is a mongoid example:

comments = post.comments
comments.update_attributes(:email => "a@b.c")

Also there is already a "Use positional operator to update all items in an array" JIRA that, once implemented, will allow you to do it in one shot.


Have you tried the $ positional operator, its the only way to update the matching sub(embedded docs) docs.


Monday, August 20, 2018

How to make a smooth live wallpaper?

I intend to create live wallpapers for iphones (iOS 9 and later) programmatically.

I used the LoveLiver project to create some live wallpapers.

I also tried the free app Live Edit on App Store to create some.

I even created some myself (by taking live photos with an iphone 6s and then set them as wallpaper).

However, all of them are not as smooth as this demo live wallpaper.

Especially the moment right after I press on the screen: For the demo, the transition from static to dynamic is very smooth. However, for all other live wallpapers I mentioned above, it's blur at fisrt, and then it becomes dynamic, hence it's not very smooth.

Do you have any idea about what is the cause of this difference?

Thank you.

Sunday, August 19, 2018

Showing WinSCP .NET assembly transfer progress on WinForm's progress bar

Have some main form on which I am calling file downloading from FTP. When this operation is raised i want to see new form as ShowDialog and progress bar on it to be shown meantime, then show the progress and close new form and back to main form. My code is working however, when it will process is started my main form freezes and after while new form is appearing and then closing. What I would like to correct is to show this new form to be showed straightaway after process is executed. Can you take a look and tell me whats wrong?

This is out of my main form the download process called:

Dim pro As New FrmProgressBarWinscp(WinScp, myremotePicturePath, ladujZdjeciaPath, True)

FrmProgressBarWinscp is as follows:

Public Class FrmProgressBarWinscp

    Property _winScp As WinScpOperation
    Property _remotePicture As String
    Property _ladujZdjecia As String
    Property _removesource As String

    Public Sub New()
        InitializeComponent()
    End Sub

    Sub New(winscp As WinScpOperation, remotePicture As String, ladujzdjecia As String, removesource As Boolean)
        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.
        _winScp = winscp
        _remotePicture = remotePicture
        _ladujZdjecia = ladujzdjecia
        _removesource = removesource
        ShowDialog()
    End Sub

    Sub Run()

        Try
            Cursor = Cursors.WaitCursor
            _winScp.GetFile(_remotePicture, _ladujZdjecia, _removesource)
            ProgressBar1.Minimum = 0
            ProgressBar1.Maximum = 1
            ProgressBar1.Value = 0
            Do
                ProgressBar1.Value = WinScpOperation._lastProgress
                ProgressBar1.Refresh()
            Loop Until ProgressBar1.Value = 1
            Cursor = Cursors.Default
            'Close()

        Catch ex As Exception

        Finally
            If _winScp IsNot Nothing Then
                _winScp.SessionDispose()
            End If

            System.Threading.Thread.Sleep(10000)
            Close()
        End Try

    End Sub

    Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Run()
    End Sub
End Class

Winscp my own class and used methods:

...
Function GetFile(source As String, destination As String, Optional removeSource As Boolean = False)
    Dim result As Boolean = True
    Try

        session.GetFiles(source, destination, removeSource).Check()

    Catch ex As Exception
        result = False
    End Try
    Return result
End Function

Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
    'Print transfer progress
    _lastProgress = e.FileProgress

End Sub

Public Shared _lastProgress As Integer

...

Further discussion nr 3:

Main form:
 Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
                                                                                                Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)

                                                                                            End Function)


                            Dim forma As New FrmProgressBar
                            forma.ShowDialog()

Progress bar form:

Public Class FrmProgressBar

    Public Sub New()
        InitializeComponent()
    End Sub
    Sub Run()
        Try
            Do
                ProgressBar1.Value = WinScpOperation._lastProgress
                ProgressBar1.Refresh()
            Loop Until ProgressBar1.Value = 1
            Cursor = Cursors.Default

        Catch ex As Exception
        Finally
            MsgBox("before sleep")
            System.Threading.Thread.Sleep(10000)
            MsgBox("after sleep sleep")
            Close()
        End Try
    End Sub

    Private Sub FrmProgressBarWinscp_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Run()
    End Sub
End Class

Point nr. 4:

  Dim tsk As Task(Of Boolean) = Task.Factory.StartNew(Of Boolean)(Function()
                                                                                                Return WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, True)

                                                                                            End Function)


                            Dim pic As New Waiting
                            pic.ShowDialog()

                            Task.WaitAll(tsk)
                            pic.Close()

Point 5:

 Dim pic As New Waiting
                            pic.ShowDialog()
                            Dim tsk As Task = Task.Factory.StartNew(Sub() WinScp.GetFile(myremotePicturePath, ladujZdjeciaPath, pic, True))




                            Task.WaitAll(tsk)
                            'pic.Close()

In some other class (maybe didn't mentioned before this method is placed in diffrent class - my custom one)

Public Function GetFile(source As String, destination As String, formclose As InvokeCloseForm, Optional removeSource As Boolean = False) As Boolean
        Dim result As Boolean = True
        Try
            session.GetFiles(source, destination, removeSource).Check()
        Catch ex As Exception
            result = False
        End Try
        formclose.RUn()
        Return result
    End Function

Interface:

Public Interface InvokeCloseForm
    Sub RUn()
End Interface

Waiting form :

Public Class Waiting
    Implements InvokeCloseForm

    Public Sub RUn() Implements InvokeCloseForm.RUn
        Me.Close()
    End Sub
End Class

Solved

The Session.GetFiles method in blocking.

It means it returns only after the transfer finishes.

The solution is to:

  • Run the WinSCP transfer (the Session.GetFiles) in a separate thread, not to block the GUI thread.

    For that see WinForm Application UI Hangs during Long-Running Operation

  • Handle the Session.FileTransferProgress event.

    Though note that the event handler will be called on the background thread, so you cannot update the progress bar directly from the handler. You have to use the Control.Invoke to make sure the progress bar is updated on the GUI thread.

    For that see How to update the GUI from another thread in C#?

  • A trivial implementation is like:

    Public Class ProgressDialog1
    
        Private Sub ProgressDialog1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
            ' Run download on a separate thread
            ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf Download))
        End Sub
    
        Private Sub Download(stateInfo As Object)
            ' Setup session options
            Dim mySessionOptions As New SessionOptions
            With mySessionOptions
                ... 
            End With
    
            Using mySession As Session = New Session
                AddHandler mySession.FileTransferProgress, AddressOf SessionFileTransferProgress
    
                ' Connect
                mySession.Open(mySessionOptions)
    
                mySession.GetFiles(, ).Check()
            End Using
    
            ' Close form (invoked on GUI thread)
            Invoke(New Action(Sub() Close()))
        End Sub
    
        Private Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
            ' Update progress bar (on GUI thread)
            ProgressBar1.Invoke(New Action(Of Double)(AddressOf UpdateProgress), e.OverallProgress)
        End Sub
    
        Private Sub UpdateProgress(progress As Double)
            ProgressBar1.Value = progress * 100
        End Sub
    End Class
    
  • You may want to disable the progress form (or its parts) during the operation, if you want to prevent the user from doing some operations.

    Use the .Enabled property of the form or control(s).


Easier, but hacky and generally not recommendable solution, is to call the Application.DoEvents method from your existing SessionFileTransferProgress handler.

And of course, you have to update the progress bar from the the SessionFileTransferProgress as well.

Private Shared Sub SessionFileTransferProgress(sender As Object, e As FileTransferProgressEventArgs)
    'Print transfer progress
    ProgressBar1.Value = e.FileProgress
    Application.DoEvents
End Sub

And the progress bar's .Minimum and .Maximum must be set before the Session.GetFiles.

But do not do that! That's a wrong approach.

And still, you need to disable the forms/controls the same way as in the correct solution above.


Saturday, August 18, 2018

Trying to SORT table on Expression

I have two tables in my report. Grouped on fields DatePaidFinancialYear and then SupplierName.

I have removed both sorts on the groups themselves. There is also no sort on the tablix.

I then have a COUNT IF in one table that does the following - =Count(IIF(Fields!DaysLateCategory.Value = "Over10Days" , 1, Nothing)) and the below in the other =Count(IIF(Fields!DaysLateCategory.Value = "Over30Days" , 1, Nothing))

This gives me below -

enter image description here

I want to sort so that the highest number is at the top. I can't work out how to do it.

When I try and sort by my counts via the Tablix - I get the following error -

A sort expression for the tablix 'Tablix5' includes an aggregate function. Aggregate functions cannot be used in data row sort expressions.

Please advise

Solved

IRC - you should remove the table sort and sort using the same expression on the Group properties

Excellent. I could have sworn I tried this but obviously not. All working now.

Thanks