-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.fs
More file actions
165 lines (136 loc) · 6.52 KB
/
Main.fs
File metadata and controls
165 lines (136 loc) · 6.52 KB
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
namespace QBLint
open System.IO
open Google.Apis.Drive.v3
open Google.Apis.Docs.v1
open Google.Apis.Docs.v1.Data
open QBLint.Lib
open QBLint.Lib.Actions
open org.languagetool
open org.languagetool.language
open org.languagetool.tools
open Avalonia.Controls
open Avalonia.FuncUI
open Avalonia.FuncUI.DSL
open Avalonia.Layout
type State =
{ Config: Config
Output: IWritable<string> }
member this.Lint(config: Config) =
async {
this.Output.Set "Starting..."
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let parentId = config.Google.FolderId
let ctx = System.Threading.SynchronizationContext.Current
do! Async.SwitchToThreadPool()
let clientService = Authorization.authorize config.Google.Email
let driveService = new DriveService(clientService)
let docsService = new DocsService(clientService)
let docsIds =
(getFiles driveService parentId (MimeType.Google(Doc))).Files
|> Seq.choose (fun doc ->
match doc.Id with
| null -> None
| id -> Some(id))
let linterFolder =
let maybeFolder =
(getFiles driveService parentId (MimeType.Google(Folder))).Files
|> Seq.tryPick (fun folder ->
if folder.Name[..5] = "qblint" then Some(folder) else None)
let name = $"""qblint.{System.DateTime.Now.ToString("s")}"""
match maybeFolder with
| None -> createFolder driveService parentId name
| Some folder ->
trashFile driveService folder.Id |> ignore
createFolder driveService parentId name
let lt = MultiThreadedJLanguageTool(new AmericanEnglish())
let customRulesLoader = rules.patterns.PatternRuleLoader()
let customRulesFile = Path.Combine(config.LanguageTool.CustomRules)
use fs = File.OpenRead(customRulesFile)
use isw = new ikvm.io.InputStreamWrapper(fs)
let customRules = customRulesLoader.getRules (isw, customRulesFile)
let iter: java.util.Iterator = customRules.iterator ()
while iter.hasNext () do
lt.addRule (iter.next () :?> rules.Rule)
Seq.iter (fun s -> lt.disableRule (s)) (config.LanguageTool.DisableRules)
do! Async.SwitchToContext(ctx)
let getText (doc: Document) =
let paragraphs =
fun (element: StructuralElement) ->
match element.Paragraph with
| null -> None
| pa -> Some(pa.Elements)
let textruns =
fun (element: ParagraphElement) ->
match element.TextRun with
| null -> None
| tr -> Some(tr.Content)
doc.Body.Content
|> Seq.choose paragraphs
|> Seq.concat
|> Seq.choose textruns
|> Seq.fold (+) ""
for docId in docsIds do
do! Async.SwitchToThreadPool()
let doc = docsService.Documents.Get(docId).Execute()
let txt = doc |> getText
let matches = lt.check txt
let dt = DetectedLanguage(lt.getLanguage (), lt.getLanguage ())
let serializer = RuleMatchesAsJsonSerializer()
let json = serializer.ruleMatchesToJson (matches, txt, 50, dt)
let upload = uploadFile driveService linterFolder.Id docId json
do! Async.SwitchToContext(ctx)
this.Output.Set(
if (upload.Status = Google.Apis.Upload.UploadStatus.Completed) then
[ this.Output.Current; $"{doc.Title} linted and uploaded." ]
else
[ this.Output.Current
$"Error while uploading {doc.Title}: {upload.Exception.Message}" ]
|> String.concat "\n"
)
stopWatch.Stop()
let time = sprintf "Executed in %f seconds." stopWatch.Elapsed.TotalSeconds
this.Output.Set([ this.Output.Current; time ] |> String.concat "\n")
}
static member Validate(config: Config) =
match Config.validate (config) with
| Ok _ -> "Configuration validated. Click Run to lint."
| Error e -> e |> String.concat "\n"
static member Init() =
let config = Config.load ()
let output: IWritable<string> = new State<string>(State.Validate config)
{ Config = config; Output = output }
[<RequireQualifiedAccess>]
module State =
let shared = State.Init()
[<AbstractClass; Sealed>]
type Views =
static member mainView() =
Component(fun ctx ->
let config = ctx.useState State.shared.Config
let output = ctx.usePassed State.shared.Output
DockPanel.create
[ DockPanel.margin 10.0
DockPanel.children
[ TextBox.create
[ TextBox.dock Dock.Top
TextBox.watermark "Email"
TextBox.text (string config.Current.Google.Email)
TextBox.onTextChanged (fun txt ->
config.Current.Google.Email <- txt
config.Current.Save(@"Config.yaml")
State.Validate config.Current |> output.Set) ]
TextBox.create
[ TextBox.dock Dock.Top
TextBox.watermark "Folder ID"
TextBox.text (string config.Current.Google.FolderId)
TextBox.onTextChanged (fun txt ->
config.Current.Google.FolderId <- txt
config.Current.Save(@"Config.yaml")
State.Validate config.Current |> output.Set) ]
TextBlock.create
[ TextBlock.dock Dock.Top; TextBlock.text (string output.Current) ]
Button.create
[ Button.dock Dock.Bottom
Button.onClick (fun _ ->
State.shared.Lint config.Current |> Async.StartImmediate)
Button.content "Run" ] ] ])