From d4c0a44429971ee8f6eeebc1201f358e383c6e69 Mon Sep 17 00:00:00 2001 From: Steffen Forkmann Date: Mon, 7 Dec 2015 14:41:00 +0100 Subject: [PATCH 1/2] Creating a Top 10 snippets graph --- App.config | 16 +- FsSnip.WebSite.fsproj | 135 +++----- Google.DataTable.Net.Wrapper.XML | 565 +++++++++++++++++++++++++++++++ app.fsx | 5 + code/common/graphs.fs | 7 + code/pages/tag.fs | 27 +- paket.dependencies | 3 +- paket.lock | 20 +- paket.references | 3 +- templates/tags.html | 12 + 10 files changed, 689 insertions(+), 104 deletions(-) create mode 100644 Google.DataTable.Net.Wrapper.XML create mode 100644 code/common/graphs.fs diff --git a/App.config b/App.config index 5b62193..6b25b29 100644 --- a/App.config +++ b/App.config @@ -1,14 +1,14 @@ - + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/FsSnip.WebSite.fsproj b/FsSnip.WebSite.fsproj index e14370c..fe1efcd 100644 --- a/FsSnip.WebSite.fsproj +++ b/FsSnip.WebSite.fsproj @@ -1,4 +1,4 @@ - + @@ -66,6 +66,7 @@ + @@ -80,6 +81,9 @@ + + True + Always @@ -117,7 +121,7 @@ - + packages\DotLiquid\lib\NET45\DotLiquid.dll @@ -128,7 +132,7 @@ - + packages\FSharp.Azure.StorageTypeProvider\lib\net40\FSharp.Azure.StorageTypeProvider.dll @@ -148,7 +152,7 @@ - + packages\FSharp.Compiler.Service\lib\net45\FSharp.Compiler.Service.dll @@ -159,7 +163,7 @@ - + packages\FSharp.Data\lib\net40\FSharp.Data.dll @@ -182,7 +186,7 @@ - + packages\FSharp.Formatting\lib\net40\CSharpFormat.dll @@ -228,7 +232,7 @@ - + packages\FSharpVSPowerTools.Core\lib\net45\FSharpVSPowerTools.Core.dll @@ -269,7 +273,7 @@ - + packages\FsPickler\lib\net45\FsPickler.dll @@ -285,8 +289,15 @@ + + + packages\Google.DataTable.Net.Wrapper\lib\Google.DataTable.Net.Wrapper.dll + True + True + + - + packages\Microsoft.Azure.KeyVault.Core\lib\net40\Microsoft.Azure.KeyVault.Core.dll @@ -295,7 +306,7 @@ - + packages\Microsoft.Azure.KeyVault.Core\lib\portable-net45+wp8+wpa81+win\Microsoft.Azure.KeyVault.Core.dll @@ -306,7 +317,7 @@ - + packages\Microsoft.Data.Edm\lib\net40\Microsoft.Data.Edm.dll @@ -315,16 +326,7 @@ - - - - packages\Microsoft.Data.Edm\lib\sl4\Microsoft.Data.Edm.dll - True - True - - - - + packages\Microsoft.Data.Edm\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.Edm.dll @@ -333,18 +335,9 @@ - - - - packages\Microsoft.Data.Edm\lib\portable-net40+sl5+wp8+win8+wpa\Microsoft.Data.Edm.dll - True - True - - - - + packages\Microsoft.Data.OData\lib\net40\Microsoft.Data.OData.dll @@ -353,16 +346,7 @@ - - - - packages\Microsoft.Data.OData\lib\sl4\Microsoft.Data.OData.dll - True - True - - - - + packages\Microsoft.Data.OData\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.OData.dll @@ -373,7 +357,7 @@ - + packages\Microsoft.Data.Services.Client\lib\net40\Microsoft.Data.Services.Client.dll @@ -382,32 +366,26 @@ - + + + - - packages\Microsoft.Data.Services.Client\lib\sl4\Microsoft.Data.Services.Client.dll + + packages\Newtonsoft.Json\lib\net35\Newtonsoft.Json.dll True True - - True - - - True - - + - - packages\Microsoft.Data.Services.Client\lib\portable-net45+wp8+win8+wpa\Microsoft.Data.Services.Client.dll + + packages\Newtonsoft.Json\lib\net20\Newtonsoft.Json.dll True True - - @@ -417,7 +395,7 @@ - + packages\Newtonsoft.Json\lib\net45\Newtonsoft.Json.dll @@ -426,7 +404,7 @@ - + packages\Newtonsoft.Json\lib\portable-net45+wp80+win8+wpa81+dnxcore50\Newtonsoft.Json.dll @@ -435,7 +413,7 @@ - + packages\Newtonsoft.Json\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll @@ -446,7 +424,7 @@ - + packages\Suave\lib\net40\Suave.dll @@ -457,7 +435,7 @@ - + packages\Suave.DotLiquid\lib\net40\Suave.DotLiquid.dll @@ -468,7 +446,7 @@ - + packages\System.Spatial\lib\net40\System.Spatial.dll @@ -477,16 +455,7 @@ - - - - packages\System.Spatial\lib\sl4\System.Spatial.dll - True - True - - - - + packages\System.Spatial\lib\portable-net45+wp8+win8+wpa\System.Spatial.dll @@ -495,15 +464,6 @@ - - - - packages\System.Spatial\lib\portable-net40+sl5+wp8+win8+wpa\System.Spatial.dll - True - True - - - @@ -521,7 +481,7 @@ - + packages\WindowsAzure.Storage\lib\net40\Microsoft.WindowsAzure.Storage.dll @@ -570,6 +530,17 @@ + + + + + packages\XPlot.GoogleCharts\lib\net45\XPlot.GoogleCharts.dll + True + True + + + + diff --git a/Google.DataTable.Net.Wrapper.XML b/Google.DataTable.Net.Wrapper.XML new file mode 100644 index 0000000..0236a9d --- /dev/null +++ b/Google.DataTable.Net.Wrapper.XML @@ -0,0 +1,565 @@ + + + + Google.DataTable.Net.Wrapper + + + + + Each cell is an object containing an actual value of the column type, + plus an optional string-formatted version of the value that you provide. + + For example: a numeric column might be assigned the value 7 + and the formatted value "seven". + If a formatted value is supplied, a chart will use the actual value for + calculations and rendering, but might show the formatted value where appropriate, + for example if the user hovers over a point. Each cell also has an optional + map of arbitrary name/value pairs. + + + + + + Adds a new property to the list of properties. + + + + + + + Removes a property from the Property Map + + + + + + Removes a property from the Property Map by an index. + + + + + + Returns a list of currently assigned properties to the Cell + + + + + Default constructor + + + + + Constructor that accepts a value + + + + + + Constructor that accepts a value and formatted properties. + + + + + + + Adds a new property to the list of properties. + + + + + + + Removes a property from the Property Map + + + + + + Removes a property from the Property Map by an index. + + + + + + Returns the value formated depending on the object type. + + + + + + + + Column type represents the type of value that the current cell holds. + + + + + [Optional] The cell value. The data ColumnType should match the column data ColumnType. + If null, the whole object should be empty and have neither v nor f properties. + + + + + [Optional] A string version of the v value, formatted for display. + The values should match, so if you specify Date(2008, 0, 1) for v, + you should specify "January 1, 2008" or some such string for this property. + This value is not checked against the v value. The visualization will not use this value + for calculation, only as a label for display. If omitted, a string version of v will be used. + + + + + Returns a list of currently assigned properties to the Cell + + + + + Returns the Json string as expected by the Google Api + + + + + + Adds a new property to the list of properties. + + + + + + + Removes a property from the Property Map + + + + + + Removes a property from the Property Map by an index. + + + + + + id [Optional] String ID of the column. Must be unique in the table. + Use basic alphanumeric characters, so the host page does not require + fancy escapes to access the column in JavaScript. Be careful not to + choose a JavaScript keyword. Example: id:'col_1' + + + + + label [Optional] String value that some visualizations display for this column. + Example: label:'Height' + + + + + ColumnType [Required] Data ColumnType of the data in the column. + Supports the following string values (examples include the v: property, described later): + + + + + A column role describes the purpose of the data in that column: + for example, a column might hold data describing tooltip text, + data point annotations, or uncertainty indicators. + + + More info about the role can be read here + https://developers.google.com/chart/interactive/docs/roles + + + + + pattern [Optional] String pattern that was used by a data source + to format numeric, date, or time column values. + This is for reference only; you probably won't need to read + the pattern, and it isn't required to exist. + The Google Visualization client does not use this value + (it reads the cell's formatted value). + If the DataTable has come from a data source in response to a query + with a format clause, the pattern you specified in that clause + will probably be returned in this value. + The recommended pattern standards are the ICU DecimalFormat and SimpleDateFormat. + + + + + + + + + p [Optional] An object that is a map of custom values applied to the cell. + These values can be of any JavaScript ColumnType. + If your visualization supports any cell-level properties, + it will describe them; otherwise, this property will be ignored. + + Example: p:{style: 'border: 1px solid green;'}. + + + + Returns a list of currently assigned properties to the Cell + + + + + Text to display on the chart near the associated data point. + The text displays without any user interaction. + Annotations and annotation text can be assigned to both + data points and categories (axis labels). + + + + + Extended text to display when the user hovers + over the associated annotation + + + + + ndicates whether a data point is certain or not. + How this is displayed depends on the chart type—for + example, it might be indicated by dashed lines or a striped fill. + + + + + Emphasizes specified chart data points. Displayed as a + thick line and/or large point. + For line and area charts, the segment + between two data points is emphasized if and only if + both data points are emphasized. + + + + + ndicates potential data range for a specific point. + Intervals are usually displayed as I-bar style range indicators. + Interval columns are numeric columns; add interval columns in + pairs, marking the low and high value of the bar. Interval + values should be added in increasing value, from left to right. + + + + + Indicates whether a point is in or out of scope. + If a point is out of scope, it is visually de-emphasized. + For line and area charts, the segment between two data points + is in scope if either endpoint is in scope. + + + + + Text to display when the user hovers over the data point + associated with this row. + + + + + You should not need to assign this role explicitly unless designing + a multi-domain chart (shown here); the basic + format of the data table enables the charting engine to infer which + columns are domain columns. However, you should be aware of + which columns are domain columns so that you know which other + columns can modify it. + Domain columns specify labels along the major axis of the chart. + Multiple domain columns can sometimes be used to support multiple + scales within the same chart. + + + + + You should not need to assign this role explicitly; the basic + format of the data table enables the charting engine to + infer which columns are domain columns. However, you sould be + aware of which columns are data columns so that you know which + other columns can modify it. + + Data role columns specify series data to render in the chart. + For most charts, one column = one series, but this can vary + by chart type (for example, scatter charts require two data + columns for the first series, and an additional one for each + additional series; candlestick charts require four data columns + for each series). + + + + + Extension class for lists. + + + + + + Creates a new column + + + + + + + + + + + Class that implements an extension for a System.Data.DataTable + + + + + Converts a System.Data.DataTable into a Google.DataTable.Net.Wrapper.DataTable + + + + + + + A DataTable represents a basic two-dimensional table. + All data (cells) in each column must have the same data type. + Each cell in the table holds a value. + + For more information about the usage of the serialized DataTable please visit: + https://developers.google.com/chart/interactive/docs/reference#DataTable + + + + + + Default constructor + + + + + Creates a new DataRow with the same schema as the table. + + A new row with the same schema as the table + + + + Adds a row to the list of rows + attached to the current DataTable + + a row created with NewRow() method + + + + + Adds a new column to the current DataTable + + + + + + + Returns a Json string compatible with the Google DataTable notation. + + + + + + Serializes the Rows. + + The choice to have inline both the row and the cell + serialization is purely for the performance reasons. + + + + + + + Serializes the Columns into the Json format + + + + + + Returns a list of already assigned columns to the current DataTable + + + + + Returns a list of already assigned rows to the current DataTable + + + + + ColumnType [Required] Data ColumnType of the data in the column. Supports the following string values (examples include the v: property, described later): + 'boolean' - JavaScript boolean value ('true' or 'false'). Example value: v:'true' + 'number' - JavaScript number value. Example values: v:7 , v:3.14, v:-55 + 'string' - JavaScript string value. Example value: v:'hello' + 'date' - JavaScript Date object (zero-based month), with the time truncated. Example value: v:new Date(2008, 0, 15) + 'datetime' - JavaScript Date object including the time. Example value: v:new Date(2008, 0, 15, 14, 30, 45) + 'timeofday' - Array of three numbers and an optional fourth, representing hour (0 indicates midnight), minute, second, and optional millisecond. Example values: v:[8, 15, 0], v: [6, 12, 1, 144] + + + + + JavaScript string value. Example value: v:'hello' + + + + + JavaScript number value. Example values: v:7 , v:3.14, v:-55 + + + + + JavaScript boolean value ('true' or 'false'). Example value: v:'true' + + + + + JavaScript Date object (zero-based month), with the time truncated. Example value: v:new Date(2008, 0, 15) + + + + + JavaScript Date object including the time. Example value: v:new Date(2008, 0, 15, 14, 30, 45) + + + + + Array of three numbers and an optional fourth, representing hour (0 indicates midnight), minute, second, and optional millisecond. Example values: v:[8, 15, 0], v: [6, 12, 1, 144] + + + + + Wraps the string around double quotes. + + + + + + + Helper class for the generic lists. + + + + + + Adds a new Column to a Google.DataTable.Net.Wrapper.DataTable + + + + + + + + Build a new Google.DataTable.Net.Wrapper.DataTable() + + + + + + Convert a List{T} to a DataTable. + + + + + Determine of specified type is nullable + + + + + Return underlying type if type is Nullable otherwise return the type + + + + + Reserved for future usages. + P values in reality should be a name -> value map + + + + + Default constructor + + + + + Constructor that has as input name and value + + + + + + + A row is an array of cells, + plus an optional map of arbitrary name/value pairs that you can assign. + + + + + Internal constructor as we don't allow the direct generation + of the row due to the fact that the table Attribute is set + internally + + + + + Adds a range of Cell objects + + + + + + Adds a single cell to the Row + + + + + + + Adds a new property to the list of properties. + + + + + + + Removes a property from the Property Map + + + + + + Removes a property from the Property Map by an index. + + + + + + A reference to the available column types of the table. + + + + + + + + + + Returns a list of currently assigned properties to the Row + + + + + Responsible for converting the System.Data.DataTable into + Google.DataTable.Net.Wrapper.DataTable. + + + + + Responsible for converting the System.Data.DataTable into + Google.DataTable.Net.Wrapper.DataTable. + + + + + For more info about System.Data.DataColumn Types + http://msdn.microsoft.com/en-us/library/system.data.datacolumn.datatype.aspx + + + + + + diff --git a/app.fsx b/app.fsx index ca8986c..8ed4f41 100644 --- a/app.fsx +++ b/app.fsx @@ -5,6 +5,10 @@ #r "packages/Suave.DotLiquid/lib/net40/Suave.DotLiquid.dll" #load "packages/FSharp.Azure.StorageTypeProvider/StorageTypeProvider.fsx" #load "packages/FSharp.Formatting/FSharp.Formatting.fsx" +#I "packages/Google.DataTable.Net.Wrapper/lib" +#I "packages/XPlot.GoogleCharts/lib/net45" +#r "XPlot.GoogleCharts.dll" +open XPlot.GoogleCharts open System open System.Web open System.IO @@ -27,6 +31,7 @@ open FSharp.Azure.StorageTypeProvider #load "code/common/utils.fs" #load "code/common/filters.fs" #load "code/common/data.fs" +#load "code/common/graphs.fs" #load "code/common/rssfeed.fs" #load "code/pages/home.fs" #load "code/pages/insert.fs" diff --git a/code/common/graphs.fs b/code/common/graphs.fs new file mode 100644 index 0000000..fec84cb --- /dev/null +++ b/code/common/graphs.fs @@ -0,0 +1,7 @@ +module FsSnip.Graphs + +open System + +type Graph = + { Id: string + Script: string } \ No newline at end of file diff --git a/code/pages/tag.fs b/code/pages/tag.fs index 32887e8..d681105 100644 --- a/code/pages/tag.fs +++ b/code/pages/tag.fs @@ -7,6 +7,8 @@ open System open System.Web open FsSnip.Utils open FsSnip.Data +open FsSnip.Graphs +open XPlot.GoogleCharts // ------------------------------------------------------------------------------------------------- // Tag page - domain model @@ -25,19 +27,36 @@ type TagModel = Snippets : seq } type AllTagsModel = - { Taglinks: TagLinks} + { Taglinks: TagLinks + Graph: Graph } let getAllTags () = - let links = + let sorted = publicSnippets |> Seq.collect (fun s -> s.Tags) |> Seq.countBy id |> Seq.sortBy (fun (_, c) -> -c) + |> Seq.cache + + let links = + sorted |> Seq.withSizeBy snd |> Seq.map (fun ((n,c),s) -> { Text = n; Size = 80 + s; Count = c; Link = HttpUtility.UrlEncode(n) }) - {Taglinks = links} + + let image = + [ (sorted |> Seq.take 10) ] + |> Chart.Bar + |> Chart.WithOptions (Options(title = "Top 10 tags")) + |> Chart.WithLabels ["Count"] + |> Chart.WithLegend true + |> Chart.WithSize (600, 250) + + { Taglinks = links + Graph = + { Id = image.Id + Script = image.Js }} // ------------------------------------------------------------------------------------------------- // Suave web parts @@ -57,7 +76,7 @@ let showAll = delay (fun () -> // Composed web part to be included in the top-level route let webPart = - choose + choose [ path "/tags/" >>= showAll pathScan "/tags/%s" showSnippets ] \ No newline at end of file diff --git a/paket.dependencies b/paket.dependencies index d795157..1af8072 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -7,4 +7,5 @@ nuget FSharp.Data nuget FSharp.Compiler.Service nuget DotLiquid nuget FSharp.Formatting >= 2.10.3 -nuget FSharp.Azure.StorageTypeProvider \ No newline at end of file +nuget FSharp.Azure.StorageTypeProvider +nuget XPlot.GoogleCharts \ No newline at end of file diff --git a/paket.lock b/paket.lock index 6535e62..e1fa492 100644 --- a/paket.lock +++ b/paket.lock @@ -15,24 +15,28 @@ NUGET FSharpVSPowerTools.Core (1.9.0) FSharp.Compiler.Service (>= 0.0.90) FsPickler (1.3.7) - Microsoft.Azure.KeyVault.Core (1.0.0) - framework: wpv8.0, >= net40 - Microsoft.Data.Edm (5.6.4) - Microsoft.Data.OData (5.6.4) - framework: winv4.5, wpv8.1, wpv8.0, >= net40 + Google.DataTable.Net.Wrapper (3.1.2) + Microsoft.Azure.KeyVault.Core (1.0.0) - framework: >= net40, wpv8.0 + Microsoft.Data.Edm (5.6.4) - framework: >= net40, winv4.5, wpv8.0 + Microsoft.Data.OData (5.6.4) - framework: >= net40, winv4.5, wpv8.0 Microsoft.Data.Edm (5.6.4) System.Spatial (5.6.4) Microsoft.Data.Services.Client (5.6.4) - framework: >= net40 Microsoft.Data.OData (5.6.4) - Newtonsoft.Json (7.0.1) - framework: wpv8.0, >= net40 + Newtonsoft.Json (7.0.1) Suave (0.31.2) FSharp.Core (>= 3.1.2.5) FsPickler (>= 1.2.5) Suave.DotLiquid (0.31.2) DotLiquid (>= 1.8.0) Suave (>= 0.31.2) - System.Spatial (5.6.4) + System.Spatial (5.6.4) - framework: >= net40, winv4.5, wpv8.0 WindowsAzure.Storage (5.0.2) - Microsoft.Azure.KeyVault.Core (>= 1.0.0) - framework: wpv8.0, >= net40 - Microsoft.Data.OData (>= 5.6.4) - framework: winv4.5, wpv8.1, wpv8.0, >= net40 + Microsoft.Azure.KeyVault.Core (>= 1.0.0) - framework: >= net40, wpv8.0 + Microsoft.Data.OData (>= 5.6.4) - framework: >= net40, winv4.5, wpv8.0 Microsoft.Data.Services.Client (>= 5.6.4) - framework: >= net40 - Newtonsoft.Json (>= 6.0.8) - framework: wpv8.0, >= net40 + Newtonsoft.Json (>= 6.0.8) - framework: >= net40, wpv8.0 + XPlot.GoogleCharts (1.2.2) + Google.DataTable.Net.Wrapper + Newtonsoft.Json Zlib.Portable (1.11.0) - framework: portable-net40+sl50+wp80+win80 diff --git a/paket.references b/paket.references index b9e4b48..cb8a866 100644 --- a/paket.references +++ b/paket.references @@ -3,4 +3,5 @@ Suave.DotLiquid FSharp.Data DotLiquid FSharp.Formatting -FSharp.Azure.StorageTypeProvider \ No newline at end of file +FSharp.Azure.StorageTypeProvider +XPlot.GoogleCharts \ No newline at end of file diff --git a/templates/tags.html b/templates/tags.html index 41fbf5d..9003c0b 100644 --- a/templates/tags.html +++ b/templates/tags.html @@ -3,12 +3,24 @@ All Tags | F# Snippets {% endblock %} +{% block customPageScripts %} + + +{% endblock %} + + {% block content %}

Snippets by Tag

+ +
+

{% for it in model.TagLinks %} {{ it.Text }} ({{ it.Count }}) From eba373f027b8d46a6936e3a65819b674ce5e845e Mon Sep 17 00:00:00 2001 From: Steffen Forkmann Date: Mon, 7 Dec 2015 17:05:14 +0100 Subject: [PATCH 2/2] Show top 10 authors --- code/pages/author.fs | 27 +++++++++++++++++++++++---- templates/authors.html | 11 +++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/code/pages/author.fs b/code/pages/author.fs index 9738918..a47c6ea 100644 --- a/code/pages/author.fs +++ b/code/pages/author.fs @@ -7,6 +7,8 @@ open System open System.Web open FsSnip.Utils open FsSnip.Data +open FsSnip.Graphs +open XPlot.GoogleCharts // ------------------------------------------------------------------------------------------------- // Author page - domain model @@ -25,20 +27,37 @@ type AuthorModel = Snippets : seq } type AllAuthorsModel = - { Authors: AuthorLinks} + { Authors: AuthorLinks + Graph : Graph } let getAllAuthors () = - let links = + let sorted = publicSnippets |> Seq.map (fun s -> s.Author) |> Seq.countBy id |> Seq.sortBy (fun (_, c) -> -c) + |> Seq.cache + + let links = + sorted |> Seq.withSizeBy snd |> Seq.map (fun ((n,c),s) -> { Text = n; Size = 80 + s; Count = c; Link = HttpUtility.UrlEncode(n) }) - { Authors = links } + + let image = + [ (sorted |> Seq.take 10) ] + |> Chart.Bar + |> Chart.WithOptions (Options(title = "Top 10 authors")) + |> Chart.WithLabels ["Count"] + |> Chart.WithLegend true + |> Chart.WithSize (600, 250) + + { Authors = links + Graph = + { Id = image.Id + Script = image.Js }} // ------------------------------------------------------------------------------------------------- // Suave web parts @@ -58,7 +77,7 @@ let showAll = delay (fun () -> DotLiquid.page "authors.html" (getAllAuthors())) // Composed web part to be included in the top-level route -let webPart = +let webPart = choose [ path "/authors/" >>= showAll pathScan "/authors/%s" showSnippets ] diff --git a/templates/authors.html b/templates/authors.html index 1a74476..246f430 100644 --- a/templates/authors.html +++ b/templates/authors.html @@ -3,6 +3,14 @@ All Authors | F# Snippets {% endblock %} +{% block customPageScripts %} + + +{% endblock %} + {% block content %}

Snippets by Author

@@ -10,6 +18,9 @@

+ +

+ {% for it in model.authors %} {{ it.Text }} ({{ it.Count }}) {% endfor %}