October 2014

Sun Mon Tue Wed Thu Fri Sat
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31  










« SortOnVersion & ScriptPro | Main | A 2nd Photo Scene Editor/Photofly Competition »

October 13, 2010

SortOnVersion in F#

I had a very nice surprise in my inbox, this morning. Thorsten Meinecke, from GTB in Berlin, decided to convert the VB.NET code contained in the last post into an F# script and to share it with this blog’s readership. Thanks, Thorsten! :-)

One thing about it being an F# script (typically stored in a .fsx file) is that it can be loaded and executed directly from the “F# Interactive” (FSI) component in Visual Studio without the need to build it into a project creating an executable. What’s also very nice is that the XAML defining the WPF dialog is embedded directly into the script, making it a simple matter to copy & paste the application into Visual Studio.

Here’s the F# code:

#I @"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.0"

#r "PresentationCore.dll"

#r "PresentationFramework.dll"

#r "WindowsBase.dll"

#r "WindowsFormsIntegration.dll"

 

open System.IO

open System.Windows.Forms

 

open System.Windows

open System.Windows.Controls

open System.Windows.Markup

open System.Windows.Media

 

let xaml =

  "<Window

    xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'

    xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'

    Title='Sort On Drawing Version' Height='327' Width='511'

      WindowStartupLocation='CenterScreen'>

    <Grid Height='Auto'>

    <Grid.ColumnDefinitions>

      <ColumnDefinition Width='270*' />

      <ColumnDefinition Width='270*' />

    </Grid.ColumnDefinitions>

    <Label Grid.ColumnSpan='2' Height='25' HorizontalAlignment='Left'

      Name='Label1' VerticalAlignment='Top'

      Width='77'>Root folder:</Label>

    <TextBox Grid.ColumnSpan='2' Height='26' Margin='73,-1,166,0'

      Name='FolderBox' VerticalAlignment='Top' />

    <Button Height='29' Margin='5,0,0,33' Name='CopyButton'

      VerticalAlignment='Bottom' IsEnabled='False'>Copy</Button>

    <Button Grid.ColumnSpan='2' Height='28' Margin='0,0,85,0'

      Name='BrowseButton' VerticalAlignment='Top'  Width='75'

      HorizontalAlignment='Right'>Browse...</Button>

    <ListView Grid.ColumnSpan='2' Margin='5,30,6,68'

      Name='FileList'/>

    <Button Height='28' HorizontalAlignment='Right' Margin='0,0,6,0'

      Name='ListButton' VerticalAlignment='Top' Width='73'

      IsEnabled='False' Grid.Column='1'>List</Button>

    <ProgressBar Grid.ColumnSpan='2' Height='28' Margin='5,0,6,2'

      Name='SortProgress' VerticalAlignment='Bottom' />

    <Button Grid.Column='1' Height='29' Margin='0,0,6,33'

      Name='MoveButton' VerticalAlignment='Bottom'

      IsEnabled='False'>Move</Button>

    </Grid>

  </Window>"

 

let (?) (this : Control) (prop: string) : 'T =

  this.FindName prop :?> 'T

let (+=) e f = Observable.add f e

 

let window = XamlReader.Parse xaml :?> Window

let label1: Label = window?Label1

let folderBox: TextBox = window?FolderBox

let copyButton: Button = window?CopyButton

let browseButton: Button = window?BrowseButton

let fileList: ListView = window?FileList

let listButton: Button = window?ListButton

let sortProgress: ProgressBar = window?SortProgress

let moveButton: Button = window?MoveButton

 

let getVersion fn =

 

  let (|StartsWith|_|) arg (s: string) =

    if s.StartsWith arg then Some() else None

 

  let data = Array.create 6 0uy

  use fs = File.OpenRead fn

  try

    if fs.Read(data, 0, 6) = 6 then

      match (new System.Text.ASCIIEncoding()).GetString data with

      | StartsWith "MC0.0" -> "R1.0"

      | StartsWith "AC1.2" -> "R1.2"

      | StartsWith "AC1.40" -> "R1.4"

      | StartsWith "AC1.50" -> "R2.05"

      | StartsWith "AC2.10" -> "R2.10"

      | StartsWith "AC2.21" -> "R2.21"

      | StartsWith "AC2.22" -> "R2.22"

      | StartsWith "AC1001" -> "R2.22"

      | StartsWith "AC1002" -> "R2.5"

      | StartsWith "AC1003" -> "R2.6"

      | StartsWith "AC1004" -> "R9"

      | StartsWith "AC1006" -> "R10"

      | StartsWith "AC1009" -> "R11"

      | StartsWith "AC1012" -> "R13"

      | StartsWith "AC1014" -> "R14"

      | StartsWith "AC1015" -> "2000"

      | StartsWith "AC1018" -> "2004"

      | StartsWith "AC1021" -> "2007"

      | StartsWith "AC1024" -> "2010"

      | _ -> "Unknown"

    else ""

  finally

    fs.Close()

 

let messageBox (s: string) =

  MessageBox.Show(s, "Sort On Drawing Version" ) |> ignore

 

let sortFiles move =

  let numSorted = ref 0

  let numSkipped = ref 0

  try

    if fileList.Items.Count = 0 then

      "Nothing to sort!" |> messageBox

    else

      sortProgress.Minimum <- 0.

      sortProgress.Maximum <- fileList.Items.Count - 1 |> float

      sortProgress.Value <- 0.

 

      for fn in Seq.cast fileList.Items do

        let ver = getVersion fn

        if not(System.String.IsNullOrEmpty ver) then

          let loc = Path.Combine(folderBox.Text, ver)

          if not(Directory.Exists loc) then

            Directory.CreateDirectory loc |> ignore

 

          let dest = Path.Combine(loc, Path.GetFileName fn)

          if not(File.Exists dest) then

            if move then

              File.Move(fn, dest)

            else

              File.Copy(fn, dest)

 

            incr numSorted

          else

            incr numSkipped

 

        sortProgress.Value <- sortProgress.Value + 1.

 

      System.String.Format(

        "{0} file{1} {2}, {3} (already existing) file{4} skipped.",

        !numSorted,

        (if !numSorted = 1 then "" else "s"),

        (if move then "moved" else "copied"),

        !numSkipped,

        (if !numSkipped = 1 then "" else "s" ) )

      |> messageBox

 

      sortProgress.Value <- 0.

      fileList.ItemsSource <- null

 

  with ex ->

    "A problem was found while sorting files: " + ex.Message

    |> messageBox

 

browseButton.Click +=

  fun _ ->

    let fbd =

      new FolderBrowserDialog(

        Description =

          "Select the root folder for the DWG version sort:" )

    if Directory.Exists folderBox.Text then

      fbd.SelectedPath <- folderBox.Text

    let dr = fbd.ShowDialog()

    if dr = DialogResult.OK then

      folderBox.Text <- fbd.SelectedPath

 

folderBox.TextChanged +=

  fun e ->

    let tb = e.Source :?> TextBox

    listButton.IsEnabled <- Directory.Exists tb.Text

 

listButton.Click +=

  fun _ ->

    try

      fileList.ItemsSource <-

        Directory.GetFiles(

          folderBox.Text, "*.dwg", SearchOption.AllDirectories )

    with _ ->

      "A problem was found accessing sub-folders in this " +

      "location: will simply get the drawings in the root " +

      "folder."

      |> messageBox

 

      fileList.ItemsSource <-

        Directory.GetFiles(folderBox.Text, "*.dwg")

 

  copyButton.IsEnabled <- true

  moveButton.IsEnabled <- true

 

moveButton.Click += fun _ -> sortFiles true

 

copyButton.Click += fun _ -> sortFiles false

 

#if COMPILED

[<System.STAThread>]

[<EntryPoint>]

let main _ = (new Application()).Run window

#else

(new Application()).Run window

#endif

The simple way to run this code, for those unfamiliar with using F# in Visual Studio, is to paste it into a file (usually a .fsx file), select the contents and use Alt-Enter to load it into FSI. That should cause the dialog shown in the previous post to display, and to work in an identical manner.

Thanks again for sharing this code, Thorsten!

blog comments powered by Disqus

Feed/Share

10 Random Posts