public static JsonValue GenerateGraph() { var nodes = Ut.NewArray(1 << _numSwitches, ix => new Node { SwitchStates = ix }); var edges = new List <Edge>(); foreach (var node in nodes) { foreach (var node2 in nodes) { if (node2 != node && isPowerOf2(node.SwitchStates ^ node2.SwitchStates)) { for (int c = 0; c < _numColors; c++) { edges.Add(new Edge(node, node2, c)); } } } } // Remove as many edges as possible without compromising reachability edges.Shuffle(); var reducedEdges = Ut.ReduceRequiredSet(edges, skipConsistencyTest: true, test: state => allNodesReachable(nodes, state.SetToTest)); return(ClassifyJson.Serialize(reducedEdges.ToList())); }
private static TranslationInfo makeDefault() { var ti = new TranslationInfo(); ti.Json = ClassifyJson.Serialize(ti).ToString(); return(ti); }
private static TSettings deserialize <TSettings>(string filename, SettingsSerializer serializer) where TSettings : SettingsBase, new() { return(Ut.WaitSharingVio(maximum: TimeSpan.FromSeconds(5), func: () => { switch (serializer) { case SettingsSerializer.ClassifyXml: return ClassifyXml.DeserializeFile <TSettings>(filename); case SettingsSerializer.ClassifyJson: return ClassifyJson.DeserializeFile <TSettings>(filename); case SettingsSerializer.ClassifyBinary: return ClassifyBinary.DeserializeFile <TSettings>(filename); case SettingsSerializer.DotNetBinary: var bf = new BinaryFormatter(); using (var fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read)) return (TSettings)bf.Deserialize(fs); default: throw new InternalErrorException("6843184"); } })); }
private void saveCore() { _file.HexagonySource = _file.Grid.ToString(); File.WriteAllText(_currentFilePath, ClassifyJson.Serialize(_file).ToStringIndented()); File.WriteAllText(sourceFilePath(), _file.HexagonySource); // _lastFileTime = File.GetLastWriteTimeUtc(_currentFilePath); _anyChanges = false; }
private void ensureModuleInfoCache() { if (_moduleInfoCache == null) { lock (this) if (_moduleInfoCache == null) { const int cols = 20; // number of icons per row const int w = 32; // width of an icon in pixels const int h = 32; // height of an icon in pixels var iconFiles = new DirectoryInfo(_config.ModIconDir).EnumerateFiles("*.png", SearchOption.TopDirectoryOnly).OrderBy(file => file.Name != "blank.png").ToArray(); var rows = (iconFiles.Length + cols - 1) / cols; var coords = new Dictionary <string, (int x, int y)>(); using (var bmp = new Bitmap(w * cols, h * rows)) { using (var g = Graphics.FromImage(bmp)) { for (int i = 0; i < iconFiles.Length; i++) { using (var icon = new Bitmap(iconFiles[i].FullName)) g.DrawImage(icon, w * (i % cols), h * (i / cols)); coords.Add(Path.GetFileNameWithoutExtension(iconFiles[i].Name), (i % cols, i / cols)); } } using (var mem = new MemoryStream()) { bmp.Save(mem, ImageFormat.Png); _moduleInfoCache = new ModuleInfoCache { IconSpritePng = mem.ToArray() }; _moduleInfoCache.IconSpriteMd5 = MD5.Create().ComputeHash(_moduleInfoCache.IconSpritePng).ToHex(); var modules = new DirectoryInfo(_config.ModJsonDir) .EnumerateFiles("*.json", SearchOption.TopDirectoryOnly) .ParallelSelect(4, file => { try { var origFile = File.ReadAllText(file.FullName); var modJson = JsonDict.Parse(origFile); var mod = ClassifyJson.Deserialize <KtaneModuleInfo>(modJson); #if DEBUG var newJson = (JsonDict)ClassifyJson.Serialize(mod); var newJsonStr = newJson.ToStringIndented(); if (newJsonStr != origFile) { File.WriteAllText(file.FullName, newJsonStr); } modJson = newJson; #endif return((modJson, mod, file.LastWriteTimeUtc).Nullable()); }
private object translate(Type targetType, JsonValue value) { var remoteId = value.Safe[":remoteid"].GetIntSafe(); if (remoteId != null) { return(new proxy(targetType, _url, remoteId.Value).GetTransparentProxy()); } return(ClassifyJson.Deserialize(targetType, value)); }
public override void Init(LoggerBase log) { var original = File.ReadAllText(Settings.ConfigFile); _config = ClassifyJson.Deserialize <KtaneWebConfig>(JsonValue.Parse(original)); var rewrite = serializeConfig(); if (rewrite != original) { File.WriteAllText(Settings.ConfigFile, rewrite); } base.Init(log); _logger = log; }
private void openCore(string filePath) { if (!File.Exists(filePath)) { DlgMessage.Show("The specified file does not exist.", "Error", DlgType.Error); return; } _currentFilePath = filePath; _file = ClassifyJson.Deserialize <HCFile>(JsonValue.Parse(File.ReadAllText(_currentFilePath))); _anyChanges = false; // _lastFileTime = File.GetLastWriteTimeUtc(_currentFilePath); updateList(); rerender(); }
private static void RetrievePonyCoatColorsFromMlpWikia() { var ponyColors = ClassifyJson.DeserializeFile <Dictionary <string, string> >(_poniesJson); ponyColors.Where(kvp => kvp.Value == null).ParallelForEach(kvp => { var pony = kvp.Key; try { var client = new HClient(); var response = client.Get(@"http://mlp.wikia.com/wiki/" + pony.Replace(' ', '_')); var str = response.DataString; var doc = CQ.CreateDocument(str); var infoboxes = doc["table.infobox"]; string color = null; for (int i = 0; i < infoboxes.Length - 1; i++) { if (infoboxes[i].Cq()["th[colspan='2']"].FirstOrDefault()?.InnerText.Contains(pony) != true) { continue; } var colorTr = infoboxes[i + 1].Cq().Find("tr").Where(tr => tr.Cq().Find("td>b").Any(td => td.InnerText == "Coat")).ToArray(); if (colorTr.Length == 0 || colorTr[0].Cq().Find("td").Length != 2) { continue; } var colorSpan = colorTr[0].Cq().Find("td>span"); var styleAttr = colorSpan[0]["style"]; var m = Regex.Match(styleAttr, @"background-color\s*:\s*#([A-F0-9]{3,6})"); if (m.Success) { color = m.Groups[1].Value; } } lock (ponyColors) { ConsoleUtil.WriteLine($"{pony.Color(ConsoleColor.Cyan)} = {(color == null ? "<nope>".Color(ConsoleColor.Red) : color.Color(ConsoleColor.Green))}", null); //if (color != null) ponyColors[pony] = color ?? "?"; } } catch (Exception e) { lock (ponyColors) ConsoleUtil.WriteLine($"{pony.Color(ConsoleColor.Cyan)} = {e.Message.Color(ConsoleColor.Red)} ({e.GetType().FullName.Color(ConsoleColor.DarkRed)})", null); } }); ClassifyJson.SerializeToFile(ponyColors, _poniesJson); }
public override IMessage Invoke(IMessage rawMsg) { var msg = (IMethodCallMessage)rawMsg; var method = msg.MethodBase as MethodInfo; var args = msg.Args; if (method == null || args == null) { throw new InternalErrorException("The transparent proxy received an invalid message."); } var parameters = method.GetParameters(); var hArgs = new List <HArg> { new HArg("Method", mangledMethodName(method)), new HArg("Arguments", new JsonList(args.Select((arg, i) => parameters[i].IsOut ? null : arg is Delegate ? new JsonDict { { ":delegate", true } } : ClassifyJson.Serialize(parameters[i].ParameterType, arg)))) }; if (_objectId != -1) { hArgs.Add(new HArg("ObjectID", _objectId)); } var responseRaw = _client.Post(_url, hArgs.ToArray()); var response = ClassifyJson.Deserialize <CommunicatorResult>(responseRaw.DataJson); var responseRet = response as CommunicatorResultReturn; if (responseRet != null) { var refOut = Enumerable.Range(0, parameters.Length) .Where(i => parameters[i].ParameterType.IsByRef) .Select(i => translate(parameters[i].ParameterType.GetElementType(), responseRet.RefOutArguments[i])) .ToArray(); return(new ReturnMessage( translate(method.ReturnType, responseRet.ReturnValue), refOut, refOut.Length, msg.LogicalCallContext, msg)); } throw new NotImplementedException(); }
void IPropellerModule.Init(LoggerBase log, JsonValue settings, ISettingsSaver saver) { SettingsSaver = saver; Log = log; try { Settings = ClassifyJson.Deserialize <TSettings>(settings) ?? new TSettings(); } catch (Exception e) { Log.Exception(e); Settings = new TSettings(); } SaveSettings(); Init(); }
public static void DoGraph() { const string name = "Candidate 1"; //* var graphJson = GenerateGraph(); File.WriteAllText($@"D:\temp\Colored Switches\{name}.json", graphJson.ToStringIndented()); /*/ * var graphJson = JsonValue.Parse(File.ReadAllText($@"D:\temp\Colored Switches\{name}.json")); * /**/ var edges = ClassifyJson.Deserialize <List <Edge> >(graphJson); var nodes = edges.Select(e => e.To).Distinct().ToArray(); var edgesFrom = nodes.ToDictionary(node => node, node => new List <Edge>()); foreach (var node in nodes) { edgesFrom[node] = edges.Where(e => e.From == node).ToList(); } var covered = new bool[_numSwitches]; foreach (var edge in edges) { var b = edge.From.SwitchStates ^ edge.To.SwitchStates; var bit = 0; while (b > 1) { bit++; b >>= 1; } if (covered[bit]) { continue; } if (Enumerable.Range(0, _numColors).All(c => edges.Any(e => e.Color == c && e.From == edge.From && e.To == edge.To))) { continue; } covered[bit] = true; } Console.WriteLine(covered.JoinString(", ")); Console.WriteLine(nodes.Select(n => $"{n.SwitchStates}={edgesFrom[n].Select(e => $"{e.Color}>{e.To.SwitchStates}").JoinString("|")}").JoinString("\n")); //JsonGraphToGraphML(graphJson, name); }
/// <summary> /// Constructs a new instance of <see cref="AjaxHandler{TApi}"/>.</summary> /// <param name="options"> /// Specifies <see cref="AjaxHandler{TApi}"/>’s exception behaviour.</param> public AjaxHandler(AjaxHandlerOptions options = AjaxHandlerOptions.ReturnExceptionsWithoutMessages) { _apiFunctions = new Dictionary <string, Func <HttpRequest, TApi, JsonValue> >(); _options = options; var typeContainingAjaxMethods = typeof(TApi); foreach (var method in typeContainingAjaxMethods.GetMethods(BindingFlags.Public | BindingFlags.Instance).Where(m => m.IsDefined <AjaxMethodAttribute>())) { var parameters = method.GetParameters(); var returnType = method.ReturnType; _apiFunctions.Add(method.Name, (req, api) => { JsonDict json; var rawJson = req.Post["data"].Value; try { json = JsonDict.Parse(rawJson); } catch (Exception e) { throw new AjaxInvalidParameterDataException(rawJson, e); } var arr = new object[parameters.Length]; for (int i = 0; i < parameters.Length; i++) { var paramName = parameters[i].Name; if (parameters[i].IsOptional && !json.ContainsKey(paramName)) { arr[i] = parameters[i].DefaultValue; } else { try { arr[i] = ClassifyJson.Deserialize(parameters[i].ParameterType, json[paramName]); } catch (Exception e) { throw new AjaxInvalidParameterException(paramName, e); } } } object result; try { result = method.Invoke(api, arr); } catch (Exception e) { throw new AjaxException("Error invoking the AJAX method.", e); } if (result is JsonValue) { return((JsonValue)result); } try { return(ClassifyJson.Serialize(returnType, result)); } catch (Exception e) { throw new AjaxInvalidReturnValueException(result, returnType, e); } }); } }
// This method is called in Init() (when the server is initialized) and in pull() (when the repo is updated due to a new git commit). private void generateTranslationCache() { var path = Path.Combine(_config.BaseDir, "Translations"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } #if DEBUG ClassifyJson.SerializeToFile(TranslationInfo.Default, Path.Combine(path, "en.json")); #endif _translationCache = new DirectoryInfo(path) .EnumerateFiles("*.json", SearchOption.TopDirectoryOnly) .ParallelSelect(Environment.ProcessorCount, file => { try { var translationJson = File.ReadAllText(file.FullName); var translation = ClassifyJson.Deserialize <TranslationInfo>(JsonDict.Parse(translationJson)); translation.langCode = file.Name.Remove(file.Name.Length - 5); var newJson = ClassifyJson.Serialize(translation); translation.Json = newJson.ToString(); #if DEBUG var newJsonIndented = newJson.ToStringIndented(); if (translationJson != newJsonIndented) { File.WriteAllText(file.FullName, newJsonIndented); } #endif return(translation); } catch (Exception e) { #if DEBUG Console.WriteLine(e.Message); Console.WriteLine(e.GetType().FullName); Console.WriteLine(e.StackTrace); #endif Log.Exception(e); return(null); } }).ToDictionary(t => t.langCode, t => t); }
public static void DoStuff() { var jsonFile = @"D:\c\KTANE\KtaneStuff\DataFiles\Hexamaze\Hexamaze.json"; var maze = ClassifyJson.DeserializeFile <HexamazeInfo>(jsonFile); Console.WriteLine(areMarkingsUnique(maze, saveFiles: true)); //var dic = new Dictionary<string, int>(); //var triangles = new[] { Marking.TriangleDown, Marking.TriangleLeft, Marking.TriangleRight, Marking.TriangleUp }; //var trianglesV = new[] { Marking.TriangleLeft, Marking.TriangleRight }; //var trianglesE = new[] { Marking.TriangleDown, Marking.TriangleUp }; //foreach (var center in Hex.LargeHexagon(9)) //{ // var countC = Hex.LargeHexagon(4).Select(h => maze.Markings.Get(center + h, Marking.None)).Count(m => m == Marking.Circle); // var countH = Hex.LargeHexagon(4).Select(h => maze.Markings.Get(center + h, Marking.None)).Count(m => m == Marking.Hexagon); // var countTV = Hex.LargeHexagon(4).Select(h => maze.Markings.Get(center + h, Marking.None)).Count(m => trianglesV.Contains(m)); // var countTE = Hex.LargeHexagon(4).Select(h => maze.Markings.Get(center + h, Marking.None)).Count(m => trianglesE.Contains(m)); // dic.IncSafe(new[] { countC != 0 ? countC + " circles" : null, countH != 0 ? countH + " hexagons" : null, countTV != 0 ? countTV + " vertex triangles" : null, countTE != 0 ? countTE + " edge triangles" : null }.Where(x => x != null).JoinString(", ")); //} //foreach (var kvp in dic.OrderBy(k => k.Value)) // Console.WriteLine($"{kvp.Key} = {kvp.Value} times ({kvp.Value / (double) 217 * 100:0.00}%)"); //// Create the PNG for the Paint.NET layer //const double hexWidth = 72; //var lhw = Hex.LargeWidth(4) * hexWidth; //var lhh = Hex.LargeHeight(4) * hexWidth * Hex.WidthToHeight; //GraphicsUtil.DrawBitmap((int) lhw, (int) lhh, g => //{ // g.Clear(Color.Transparent); // g.FillPolygon(new SolidBrush(Color.FromArgb(10, 104, 255)), Hex.LargeHexagonOutline(4, hexWidth).Select(p => new PointD(p.X + lhw / 2, p.Y + lhh / 2).ToPointF()).ToArray()); //}).Save(@"D:\temp\temp.png"); maze = GenerateMarkings(maze); WriteMazeInManual(maze); // Save the JSON //ClassifyJson.SerializeToFile(maze, jsonFile); }
internal static void serialize(object settings, Type settingsType, string filename, SettingsSerializer serializer) { var tempname = filename + ".~tmp"; Ut.WaitSharingVio(() => { switch (serializer) { case SettingsSerializer.ClassifyXml: // SerializeToFile automatically creates the folder if necessary ClassifyXml.SerializeToFile(settingsType, settings, tempname, format: ClassifyXmlFormat.Create("Settings")); break; case SettingsSerializer.ClassifyJson: // SerializeToFile automatically creates the folder if necessary ClassifyJson.SerializeToFile(settingsType, settings, tempname); break; case SettingsSerializer.ClassifyBinary: // SerializeToFile automatically creates the folder if necessary ClassifyBinary.SerializeToFile(settingsType, settings, tempname); break; case SettingsSerializer.DotNetBinary: PathUtil.CreatePathToFile(tempname); var bf = new BinaryFormatter(); using (var fs = File.Open(tempname, FileMode.Create, FileAccess.Write, FileShare.Read)) bf.Serialize(fs, settings); break; default: throw new InternalErrorException("4968453"); } File.Delete(filename); File.Move(tempname, filename); }, TimeSpan.FromSeconds(5)); }
private ModuleInfoCache getModuleInfoCache() { ModuleInfoCache mic = null; do { if (_moduleInfoCache == null) { lock (this) if (_moduleInfoCache == null) { const int cols = 20; // number of icons per row const int w = 32; // width of an icon in pixels const int h = 32; // height of an icon in pixels var iconFiles = new DirectoryInfo(Path.Combine(_config.BaseDir, "Icons")).EnumerateFiles("*.png", SearchOption.TopDirectoryOnly).OrderBy(file => file.Name != "blank.png").ToArray(); var rows = (iconFiles.Length + cols - 1) / cols; var coords = new Dictionary <string, (int x, int y)>(); using var bmp = new Bitmap(w * cols, h * rows); using (var g = Graphics.FromImage(bmp)) { for (int i = 0; i < iconFiles.Length; i++) { using (var icon = new Bitmap(iconFiles[i].FullName)) g.DrawImage(icon, w * (i % cols), h * (i / cols)); coords.Add(Path.GetFileNameWithoutExtension(iconFiles[i].Name), (i % cols, i / cols)); } } using var mem = new MemoryStream(); bmp.Save(mem, ImageFormat.Png); // This needs to be a separate variable (don’t use _moduleInfoCache itself) because that field needs to stay null until it is fully initialized var moduleInfoCache = new ModuleInfoCache { IconSpritePng = mem.ToArray() }; moduleInfoCache.IconSpriteMd5 = MD5.Create().ComputeHash(moduleInfoCache.IconSpritePng).ToHex(); // Load TP data from the spreadsheet JsonList entries; try { entries = new HClient().Get("https://spreadsheets.google.com/feeds/list/1G6hZW0RibjW7n72AkXZgDTHZ-LKj0usRkbAwxSPhcqA/1/public/values?alt=json").DataJson["feed"]["entry"].GetList(); } catch (Exception e) { Log.Exception(e); entries = new JsonList(); } var moduleLoadExceptions = new JsonList(); var modules = new DirectoryInfo(Path.Combine(_config.BaseDir, "JSON")) .EnumerateFiles("*.json", SearchOption.TopDirectoryOnly) .ParallelSelect(Environment.ProcessorCount, file => { try { var origFile = File.ReadAllText(file.FullName); var modJson = JsonDict.Parse(origFile); var mod = ClassifyJson.Deserialize <KtaneModuleInfo>(modJson); #if DEBUG var newJson = (JsonDict)ClassifyJson.Serialize(mod); var newJsonStr = newJson.ToStringIndented(); if (newJsonStr != origFile) { File.WriteAllText(file.FullName, newJsonStr); } modJson = newJson;
private HttpResponse generateJson(HttpRequest req) { if (req.Method != HttpMethod.Post) { return(HttpResponse.PlainText("Only POST requests allowed.", HttpStatusCode._405_MethodNotAllowed)); } void populateObject(object obj, Type type) { foreach (var f in type.GetFields()) { var attr = f.GetCustomAttribute <EditableFieldAttribute>(); if (attr == null) { continue; } var fType = f.FieldType; if (f.FieldType.TryGetGenericParameters(typeof(Nullable <>), out var fTypes)) { fType = fTypes[0]; } var val = req.Post[f.Name].Value; try { if (f.GetCustomAttribute <EditableNestedAttribute>() != null) { if (val != "on") { f.SetValue(obj, null); } else { var nestedObj = Activator.CreateInstance(f.FieldType); populateObject(nestedObj, f.FieldType); f.SetValue(obj, nestedObj); } continue; } if (fType == typeof(string)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? null : val.Trim()); } else if (fType.IsEnum) { var enumVal = val == null ? null : Enum.Parse(fType, val); if (enumVal != null) { f.SetValue(obj, enumVal); } } else if (fType == typeof(DateTime)) { f.SetValue(obj, DateTime.ParseExact(val, "yyyy-MM-dd", null)); } else if (fType == typeof(string[])) { f.SetValue(obj, val.Split(';').Select(str => str.Trim()).ToArray()); } else if (fType == typeof(int)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? 0 : int.Parse(val)); } else if (fType == typeof(decimal)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? 0m : decimal.Parse(val)); } else if (fType == typeof(bool)) { f.SetValue(obj, val == "on"); } else { throw new InvalidOperationException($"Unrecognized field type: {fType.FullName}"); } } catch (Exception e) { _logger.Warn($"Generate JSON: unrecognized value. Field: {f.Name}, Type: {fType}, Value: “{val ?? "<null>"}”, Exception: {e.Message} ({e.GetType().FullName})"); } } } var m = new KtaneModuleInfo(); populateObject(m, typeof(KtaneModuleInfo)); var json = ClassifyJson.Serialize(m); // Now deserialize and then re-serialize this to force KtaneModuleInfo to perform some sanity things var m2 = ClassifyJson.Deserialize <KtaneModuleInfo>(json); return(HttpResponse.PlainText(ClassifyJson.Serialize(m2).ToStringIndented())); }
public bool IsSolved(string json) { var state = JsonValue.Parse(json); var kyudokuGrids = KyudokuGrids.Split(36).Select(grid => grid.Select(ch => ch - '0').ToArray()).ToArray(); var constraints = Constraints == null ? new SvgConstraint[0] : ClassifyJson.Deserialize <SvgConstraint[]>(JsonValue.Parse(Constraints)); // Check that all cells in the Sudoku grid have a digit var sudokuDigits = new int[81]; for (int cell = 0; cell < 81; cell++) { var kyCells = Enumerable.Range(0, 4) .Where(c => cell % 9 >= 3 * (c % 2) && cell % 9 < 6 + 3 * (c % 2) && cell / 9 >= 3 * (c / 2) && cell / 9 < 6 + 3 * (c / 2)) .Select(c => (corner: c, kyCell: cell % 9 - 3 * (c % 2) + 6 * ((cell / 9) - 3 * (c / 2)))) .Where(inf => state["circledDigits"][inf.corner][inf.kyCell].Apply(v => v != null && v.GetBool())) .ToArray(); if (kyCells.Length > 1 && kyCells.Any(inf => kyudokuGrids[inf.corner][inf.kyCell] != kyudokuGrids[kyCells[0].corner][kyCells[0].kyCell])) { return(false); } else if (kyCells.Length >= 1) { sudokuDigits[cell] = kyudokuGrids[kyCells[0].corner][kyCells[0].kyCell]; } else if (state["enteredDigits"][cell] != null) { sudokuDigits[cell] = state["enteredDigits"][cell].GetInt(); } else { return(false); } } // Check the Sudoku rules (rows, columns and regions) for (var i = 0; i < 9; i++) { for (var colA = 0; colA < 9; colA++) { for (var colB = colA + 1; colB < 9; colB++) { if (sudokuDigits[colA + 9 * i] == sudokuDigits[colB + 9 * i]) { return(false); } } } for (var rowA = 0; rowA < 9; rowA++) { for (var rowB = rowA + 1; rowB < 9; rowB++) { if (sudokuDigits[i + 9 * rowA] == sudokuDigits[i + 9 * rowB]) { return(false); } } } for (var cellA = 0; cellA < 9; cellA++) { for (var cellB = cellA + 1; cellB < 9; cellB++) { if (sudokuDigits[cellA % 3 + 3 * (i % 3) + 9 * ((cellA / 3) + 3 * (i / 3))] == sudokuDigits[cellB % 3 + 3 * (i % 3) + 9 * ((cellB / 3) + 3 * (i / 3))]) { return(false); } } } } // Check the Sudoku constraints foreach (var constr in constraints) { if (!constr.Verify(sudokuDigits)) { return(false); } } // Check all of the Kyudokus for (var corner = 0; corner < 4; corner++) { var digitCounts = new int[9]; var rowSums = new int[6]; var colSums = new int[6]; for (var cell = 0; cell < 36; cell++) { if (state["circledDigits"][corner][cell].Apply(v => v != null && v.GetBool())) { digitCounts[kyudokuGrids[corner][cell] - 1]++; rowSums[cell / 6] += kyudokuGrids[corner][cell]; colSums[cell % 6] += kyudokuGrids[corner][cell]; } } if (rowSums.Any(r => r > 9) || colSums.Any(c => c > 9) || digitCounts.Any(c => c != 1)) { return(false); } } return(true); }
public override void Init(LoggerBase log) { #if DEBUG if (string.IsNullOrWhiteSpace(Settings.ConfigFile)) { var config = new KtaneWebConfig(); Console.WriteLine(); ConsoleUtil.WriteLine("It appears that you are running KtaneWeb for the first time.".Color(ConsoleColor.White)); tryAgain1: ConsoleUtil.WriteLine(@"Please provide a location for the JSON settings file (for example: {0/DarkCyan}):".Color(ConsoleColor.Gray).Fmt(@"C:\Path\KtaneWeb.settings.json")); var path = Console.ReadLine(); try { ClassifyJson.SerializeToFile(config, path); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain1; } Console.WriteLine(); tryAgain2: ConsoleUtil.WriteLine("Do you already have a local clone of the KtaneContent repository that you want the website to use?".Color(ConsoleColor.White)); Console.WriteLine("If yes, please type the full path to that repository. If no, just press Enter."); var ktaneContent = Console.ReadLine(); var expectedSubfolders = "HTML,More,JSON,Icons".Split(','); if (string.IsNullOrWhiteSpace(ktaneContent)) { ConsoleUtil.WriteLine(@"In that case we will create a new clone. I can do that automatically if you have git installed (if you don’t, please abort now).".Color(ConsoleColor.White)); ConsoleUtil.WriteLine("This will take a long time as the repository is large.".Color(ConsoleColor.White)); Console.WriteLine(); tryAgain3: ConsoleUtil.WriteLine("Please choose a path where you would like all the data stored (for example: {0/DarkCyan}):".Color(ConsoleColor.Gray).Fmt(@"C:\Path\KtaneContent")); var cloneFolder = Console.ReadLine(); try { Directory.CreateDirectory(cloneFolder); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain3; } try { config.BaseDir = Path.Combine(cloneFolder, "Public"); CommandRunner.Run("git", "clone", "https://github.com/Timwi/KtaneContent.git", config.BaseDir).Go(); config.MergedPdfsDir = Path.Combine(cloneFolder, "MergedPdfs"); Directory.CreateDirectory(config.MergedPdfsDir); config.LogfilesDir = Path.Combine(cloneFolder, "Logfiles"); Directory.CreateDirectory(config.LogfilesDir); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain2; } } else if (expectedSubfolders.Any(s => !Directory.Exists(Path.Combine(ktaneContent, s)))) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {"That folder does not appear to contain KtaneContent.".Color(ConsoleColor.Red)}", null); ConsoleUtil.WriteLine("(We’re looking for a folder that contains subfolders named: {0/DarkMagenta})".Color(ConsoleColor.Magenta).Fmt(expectedSubfolders.JoinString(", "))); goto tryAgain2; } else { var p = ktaneContent; while (p.EndsWith("\"")) { p = Path.GetDirectoryName(p); } config.BaseDir = p; p = Path.GetDirectoryName(p); Console.WriteLine(); tryAgain4: var logfiles = Path.Combine(p, "Logfiles"); ConsoleUtil.WriteLine("Please choose a path where you would like KtaneWeb to store logfiles uploaded through the Logfile Analyzer, or just press Enter to use the default ({0/DarkCyan}):".Color(ConsoleColor.Gray).Fmt(logfiles)); config.LogfilesDir = Console.ReadLine(); if (string.IsNullOrWhiteSpace(config.LogfilesDir)) { ConsoleUtil.WriteLine("Using default: {0/DarkCyan}".Color(ConsoleColor.Gray).Fmt(logfiles)); config.LogfilesDir = logfiles; } try { Directory.CreateDirectory(config.LogfilesDir); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain4; } Console.WriteLine(); tryAgain5: var mergedPdfs = Path.Combine(p, "MergedPdfs"); ConsoleUtil.WriteLine("Please choose a path where you would like KtaneWeb to store merged PDFs, or just press Enter to use the default ({0/DarkCyan}):".Color(ConsoleColor.Gray).Fmt(mergedPdfs)); config.MergedPdfsDir = Console.ReadLine(); if (string.IsNullOrWhiteSpace(config.MergedPdfsDir)) { ConsoleUtil.WriteLine("Using default: {0/DarkCyan}".Color(ConsoleColor.Gray).Fmt(mergedPdfs)); config.MergedPdfsDir = mergedPdfs; } try { Directory.CreateDirectory(config.MergedPdfsDir); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain5; } var appPath = PathUtil.AppPathCombine(@"..\.."); config.JavaScriptFile = Path.Combine(appPath, @"Src\Resources\KtaneWeb.js"); config.CssFile = Path.Combine(appPath, @"Src\Resources\KtaneWeb.css"); if (!File.Exists(config.JavaScriptFile) || !File.Exists(config.CssFile)) { Console.WriteLine(); tryAgain6: ConsoleUtil.WriteLine("Finally, please let me know where you placed the KtaneWeb source code (what you’re running right now):".Color(ConsoleColor.Gray)); appPath = Console.ReadLine(); config.JavaScriptFile = Path.Combine(appPath, @"Src\Resources\KtaneWeb.js"); config.CssFile = Path.Combine(appPath, @"Src\Resources\KtaneWeb.css"); if (!File.Exists(config.JavaScriptFile) || !File.Exists(config.CssFile)) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {"That does not look like the KtaneWeb source code folder.".Color(ConsoleColor.Red)}", null); goto tryAgain6; } } } try { ClassifyJson.SerializeToFile(config, path); Settings.ConfigFile = path; SaveSettings(); } catch (Exception e) { ConsoleUtil.WriteLine($"{"Problem:".Color(ConsoleColor.Magenta)} {e.Message.Color(ConsoleColor.Red)} {$"({e.GetType().FullName})".Color(ConsoleColor.DarkRed)}", null); goto tryAgain1; } Console.WriteLine(); ConsoleUtil.WriteLine("That should be all set up for you now!".Color(ConsoleColor.Green)); ConsoleUtil.WriteLine("Feel free to browse the settings file we just created if you’re curious.".Color(ConsoleColor.DarkGreen)); ConsoleUtil.WriteLine(@"For automatic PDF generation, we are assuming that Google Chrome is at its default location; if not, please change it manually in the JSON file.".Color(ConsoleColor.DarkGreen)); Console.WriteLine(); Console.WriteLine(); } #endif var original = File.ReadAllText(Settings.ConfigFile); _config = ClassifyJson.Deserialize <KtaneWebConfig>(JsonValue.Parse(original)); var rewrite = serializeConfig(); if (rewrite != original) { File.WriteAllText(Settings.ConfigFile, rewrite); } base.Init(log); _logger = log; }
private string serializeConfig() { return(ClassifyJson.Serialize(_config, new ClassifyOptions { SerializationEqualityComparer = new CustomEqualityComparer <object>(customComparison) }).ToStringIndented()); }
public override HttpResponse Handle(HttpRequest req) { if (Settings.DataDir == null || !Directory.Exists(Settings.DataDir)) { return(HttpResponse.Html($@"<h1>Data folder not configured.</h1>", HttpStatusCode._500_InternalServerError)); } if (req.Method == HttpMethod.Post) { return(processPost(req)); } if (req.Url["set"] != null) { var filePath = Path.Combine(Settings.DataDir, $"set-{req.Url["set"]}.json"); if (!File.Exists(filePath)) { return(HttpResponse.PlainText("That set does not exist.", HttpStatusCode._404_NotFound)); } var set = ClassifyJson.DeserializeFile <RankSet>(filePath); return(HttpResponse.Html(new HTML( new HEAD( new TITLE("Rank"), new META { name = "viewport", content = "width=device-width,initial-scale=1.0" }, new META { charset = "utf-8" }, new STYLELiteral(Resources.RankCss)), new BODY( new H1(set.Name), set.ListRankings.Count == 0 ? null : Ut.NewArray <object>( new H2($"Existing rankings"), new UL(set.ListRankings.Select(ranking => new LI(new A { href = req.Url.WithoutQuery().WithQuery("ranking", ranking.PublicToken).ToHref() }._(ranking.Title), ranking.Finished ? null : " (unfinished)")))), new FORM { action = req.Url.ToHref(), method = method.post }._( new INPUT { type = itype.hidden, name = "fnc", value = "start" }, new INPUT { type = itype.hidden, name = "set", value = set.Hash }, new H2($"Start a new ranking"), new P("Enter the title for this ranking. Preferably start with your name and specify the ranking criterion. For example: “Brian, by preference” or “Thomas, by difficulty”."), new DIV(new INPUT { name = "title", value = "Brian, by preference", accesskey = "," }), new P("Enter the question to ask for comparing each pair of items (examples: “Which do you like better?” (ranking by preference), “Which do you find harder?” (ranking by difficulty), etc.)"), new DIV(new INPUT { name = "question", value = "Which do you like better?" }), new DIV(new BUTTON { type = btype.submit, accesskey = "g" }._(new KBD("G"), "o"))))))); } if (req.Url["ranking"] != null) { var filePath = rankingPath(req.Url["ranking"]); if (!File.Exists(filePath)) { return(HttpResponse.PlainText("That ranking does not exist.", HttpStatusCode._404_NotFound)); } var ranking = ClassifyJson.DeserializeFile <RankRanking>(filePath); var setFilePath = setPath(ranking.SetHash); if (!File.Exists(setFilePath)) { return(HttpResponse.PlainText("The set does not exist.", HttpStatusCode._404_NotFound)); } var set = ClassifyJson.DeserializeFile <RankSet>(setFilePath); var canEdit = false; if (req.Url["secret"] != null) { if (req.Url["secret"] != ranking.PrivateToken) { return(HttpResponse.Redirect(req.Url.WithoutQuery("secret"))); } canEdit = true; } var(ix1, ix2, ranked) = attemptRanking(ranking, set); return(HttpResponse.Html(new HTML( new HEAD( new TITLE("Rank"), new META { name = "viewport", content = "width=device-width,initial-scale=1.0" }, new META { charset = "utf-8" }, new STYLELiteral(Resources.RankCss)), new BODY( new H1(set.Name), new H2(ranking.Title), ix1 == -1 || !canEdit ? null : new FORM { action = req.Url.ToHref(), method = method.post }._( new INPUT { type = itype.hidden, name = "fnc", value = "rank" }, new INPUT { type = itype.hidden, name = "ranking", value = ranking.PublicToken }, new INPUT { type = itype.hidden, name = "secret", value = ranking.PrivateToken }, new INPUT { type = itype.hidden, name = "ix1", value = ix1.ToString() }, new INPUT { type = itype.hidden, name = "ix2", value = ix2.ToString() }, new P { class_ = "comparison" }._(ranking.Question), new DIV { class_ = "comparison" }._(new BUTTON { type = btype.submit, name = "more", value = ix1.ToString() }._(set.Items[ix1])), new DIV { class_ = "comparison" }._(new BUTTON { type = btype.submit, name = "more", value = ix2.ToString() }._(set.Items[ix2]))), new UL(ranked.Select(ix => new LI { class_ = ranked.All(rIx => rIx == ix || ranking.Comparisons.Any(rc => (rc.Less == ix && rc.More == rIx) || (rc.Less == rIx && rc.More == ix))) ? "complete" : "incomplete" }._(set.Items[ix]))))))); } return(HttpResponse.Html(new HTML( new HEAD( new TITLE("Rank"), new META { name = "viewport", content = "width=device-width,initial-scale=1.0" }, new META { charset = "utf-8" }, new STYLELiteral(Resources.RankCss)), new BODY( new FORM { action = req.Url.ToHref(), method = method.post }._( new INPUT { type = itype.hidden, name = "fnc", value = "create" }, new H1("Rank what?"), new P("Choose a set to rank."), new UL(_setsList.Select(s => new LI(new A { href = req.Url.WithQuery("set", s.Hash).ToHref() }._(s.Name)))), new P("Or enter/paste the items that need ranking (one item per line)."), new DIV(new TEXTAREA { name = "items", accesskey = "," }), new P("Give it a name (e.g.: Episodes of “Best Show Evar”)."), new DIV(new INPUT { type = itype.text, name = "title", value = "Episodes of “Best Show Evar”" }), new DIV(new BUTTON { type = btype.submit, accesskey = "g" }._(new KBD("G"), "o"))))))); }
private void ensureModuleInfoCache() { if (_moduleInfoCache == null) { lock (this) if (_moduleInfoCache == null) { const int cols = 20; // number of icons per row const int w = 32; // width of an icon in pixels const int h = 32; // height of an icon in pixels var iconFiles = new DirectoryInfo(_config.ModIconDir).EnumerateFiles("*.png", SearchOption.TopDirectoryOnly).OrderBy(file => file.Name != "blank.png").ToArray(); var rows = (iconFiles.Length + cols - 1) / cols; var coords = new Dictionary <string, (int x, int y)>(); using var bmp = new Bitmap(w * cols, h * rows); using (var g = Graphics.FromImage(bmp)) { for (int i = 0; i < iconFiles.Length; i++) { using (var icon = new Bitmap(iconFiles[i].FullName)) g.DrawImage(icon, w * (i % cols), h * (i / cols)); coords.Add(Path.GetFileNameWithoutExtension(iconFiles[i].Name), (i % cols, i / cols)); } } using var mem = new MemoryStream(); bmp.Save(mem, ImageFormat.Png); _moduleInfoCache = new ModuleInfoCache { IconSpritePng = mem.ToArray() }; _moduleInfoCache.IconSpriteMd5 = MD5.Create().ComputeHash(_moduleInfoCache.IconSpritePng).ToHex(); // Load TP data from the spreadsheet JsonList entries; try { entries = new HClient().Get("https://spreadsheets.google.com/feeds/list/1WEzVOKxOO5CDGoqAHjJKrC-c-ZGgsTPRLXBCs8RrAwU/1/public/values?alt=json").DataJson["feed"]["entry"].GetList(); } catch (Exception e) { _logger.Exception(e); entries = new JsonList(); } var modules = new DirectoryInfo(_config.ModJsonDir) .EnumerateFiles("*.json", SearchOption.TopDirectoryOnly) .ParallelSelect(4, file => { try { var origFile = File.ReadAllText(file.FullName); var modJson = JsonDict.Parse(origFile); var mod = ClassifyJson.Deserialize <KtaneModuleInfo>(modJson); #if DEBUG var newJson = (JsonDict)ClassifyJson.Serialize(mod); var newJsonStr = newJson.ToStringIndented(); if (newJsonStr != origFile) { File.WriteAllText(file.FullName, newJsonStr); } modJson = newJson;
void IPropellerModule.Init(LoggerBase log, JsonValue settings, ISettingsSaver saver) { _log = log; _saver = saver; _settings = ClassifyJson.Deserialize <Settings>(settings) ?? new Settings(); var validPaths = new List <string>(); foreach (var path in _settings.Paths) { if (!Directory.Exists(path)) { _log.Warn(@"DocGen: Warning: The folder ""{0}"" specified in the settings does not exist. Ignoring path.".Fmt(path)); } else { validPaths.Add(path); } } _settings.Paths = validPaths.ToArray(); saver.SaveSettings(ClassifyJson.Serialize(_settings)); string copyToPath = null; if (_settings.DllTempPath != null) { // Try to clean up old folders we've created before var tempPath = _settings.DllTempPath; Directory.CreateDirectory(tempPath); foreach (var path in Directory.GetDirectories(tempPath, "docgen-tmp-*")) { try { Directory.Delete(path, true); } catch { } } // Find a new folder to put the DLL files into int j = 1; copyToPath = Path.Combine(tempPath, "docgen-tmp-" + j); while (Directory.Exists(copyToPath)) { j++; copyToPath = Path.Combine(tempPath, "docgen-tmp-" + j); } Directory.CreateDirectory(copyToPath); } _docGen = new DocumentationGenerator(_settings.Paths, _settings.RequireAuthentication ? _settings.UsernamePasswordFile ?? "" : null, copyToPath); lock (_log) { _log.Info("DocGen initialised with {0} assemblies: {1}".Fmt(_docGen.AssembliesLoaded.Count, _docGen.AssembliesLoaded.JoinString(", "))); if (_docGen.AssemblyLoadErrors.Count > 0) { _log.Warn("{0} assembly load errors:".Fmt(_docGen.AssemblyLoadErrors.Count)); foreach (var tuple in _docGen.AssemblyLoadErrors) { _log.Warn("{0} error: {1}".Fmt(tuple.Item1, tuple.Item2)); } } } }
private HttpResponse generateJson(HttpRequest req) { if (req.Method != HttpMethod.Post) { return(HttpResponse.PlainText("Only POST requests allowed.", HttpStatusCode._405_MethodNotAllowed)); } void populateObject(object obj, Type type) { foreach (var f in type.GetFields()) { var attr = f.GetCustomAttribute <EditableFieldAttribute>(); if (attr == null || attr.ReadableName == null) { continue; } var fType = f.FieldType; if (f.FieldType.TryGetGenericParameters(typeof(Nullable <>), out var fTypes)) { fType = fTypes[0]; } var val = req.Post[f.Name].Value; try { if (f.GetCustomAttribute <EditableNestedAttribute>() != null) { if (val != "on") { f.SetValue(obj, null); } else { var nestedObj = Activator.CreateInstance(f.FieldType); populateObject(nestedObj, f.FieldType); f.SetValue(obj, nestedObj); } continue; } if (fType == typeof(string)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? null : val.Trim()); } else if (fType.IsEnum && fType.GetCustomAttribute <FlagsAttribute>() != null) { var intVal = 0; foreach (var value in Enum.GetValues(fType)) { if (req.Post[$"{f.Name}-{value}"].Value != null) { intVal |= (int)value; } } f.SetValue(obj, intVal); } else if (fType.IsEnum) { var enumVal = val == null ? null : Enum.Parse(fType, val); if (enumVal != null) { f.SetValue(obj, enumVal); } } else if (fType == typeof(DateTime)) { f.SetValue(obj, DateTime.ParseExact(val, "yyyy-MM-dd", null)); } else if (fType == typeof(string[])) { f.SetValue(obj, val.Split(attr.AllowedSeparators).Select(str => str.Trim()).ToArray().Apply(list => list.Length == 0 || (list.Length == 1 && string.IsNullOrWhiteSpace(list[0])) ? null : list)); } else if (fType == typeof(Dictionary <string, string>)) { if (val.Trim() == "") { continue; } else if (!attr.AllowedDictSeparators.Any(sep => val.Contains(sep))) { f.SetValue(obj, new Dictionary <string, string>() { { attr.DefaultKey, string.IsNullOrWhiteSpace(val) ? null : val.Trim() } }); } else { f.SetValue(obj, val.Split(attr.AllowedSeparators).Select(str => str.Split(attr.AllowedDictSeparators)).ToDictionary(x => x[0].Trim(), x => x[1].Trim())); } } else if (fType == typeof(int)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? 0 : int.Parse(val)); } else if (fType == typeof(decimal)) { f.SetValue(obj, string.IsNullOrWhiteSpace(val) ? 0m : decimal.Parse(val)); } else if (fType == typeof(bool)) { f.SetValue(obj, val == "on"); } else if (fType == typeof(TutorialVideoInfo[])) { f.SetValue(obj, ClassifyJson.Deserialize <TutorialVideoInfo[]>(val)); } else { throw new InvalidOperationException($"Unrecognized field type: {fType.FullName}"); } } catch (Exception e) { Log.Warn($"Generate JSON: unrecognized value. Field: {f.Name}, Type: {fType}, Value: “{val ?? "<null>"}”, Exception: {e.Message} ({e.GetType().FullName})"); } } } var m = new KtaneModuleInfo(); populateObject(m, typeof(KtaneModuleInfo)); if (string.IsNullOrWhiteSpace(m.Name)) { return(HttpResponse.PlainText("You did not specify a module name.")); } var json = ClassifyJson.Serialize(m); // Now deserialize and then re-serialize this to force KtaneModuleInfo to perform some sanity things var m2 = ClassifyJson.Deserialize <KtaneModuleInfo>(json); return(HttpResponse.PlainText(ClassifyJson.Serialize(m2).ToStringIndented())); }
private HttpResponse PuzzlePage(HttpRequest req, DbSession session, Db db) { Match m; if ((m = Regex.Match(req.Url.Path, @"^/db-update/(\d+)$")).Success && req.Method == HttpMethod.Post) { return(dbUpdate(req, session, db, int.Parse(m.Groups[1].Value))); } var puzzleIdStr = req.Url.Path.Length == 0 ? "" : req.Url.Path.Substring(1); if (!int.TryParse(puzzleIdStr, out int puzzleId) || puzzleId < 0) { return(page404(req)); } var dbPuzzle = db.Puzzles.FirstOrDefault(p => p.PuzzleID == puzzleId); if (dbPuzzle == null || dbPuzzle.Invalid) { return(page404(req)); } var puzzle = new Kyudosudoku(dbPuzzle.KyudokuGrids.Split(36).Select(subgrid => subgrid.Select(ch => ch - '0').ToArray()).ToArray(), dbPuzzle.Constraints == null ? new SvgConstraint[0] : ClassifyJson.Deserialize <SvgConstraint[]>(JsonValue.Parse(dbPuzzle.Constraints))); var userPuzzle = session.User == null ? null : db.UserPuzzles.FirstOrDefault(up => up.UserID == session.User.UserID && up.PuzzleID == puzzleId); var extraTop = puzzle.Constraints.MaxOrDefault(c => c.ExtraTop, 0); var extraRight = puzzle.Constraints.MaxOrDefault(c => c.ExtraRight, 0); var extraLeft = puzzle.Constraints.MaxOrDefault(c => c.ExtraLeft, 0); var helpSvg = @"<g transform='translate(.05, .05) scale(.008)'> <path fill='#fcedca' stroke='black' stroke-width='2' d='M12.5 18.16h75v25h-75z'/> <text class='label' x='50' y='33.4' font-size='24' text-anchor='middle' transform='translate(0 5.66)'>???</text> <path fill='white' stroke='black' stroke-width='2' d='M53.238 33.237V73.17l9.513-9.513 7.499 18.106 5.272-2.184-7.38-17.818h13.62z'/> </g>"; var fillSvg = @"<text x='.45' y='.4' font-size='.25'>Auto</text><text x='.45' y='.65' font-size='.25' fill='hsl(217, 80%, 50%)'>123</text>"; var buttonsRight = Ut.NewArray <(string label, bool isSvg, string id, double width, int row)>(9, btn => ((btn + 1).ToString(), false, (btn + 1).ToString(), .9, 0)) .Concat(Ut.NewArray <(string label, bool isSvg, string id, double width, int row)>( ("Normal", false, "normal", 2.6, 1), ("Corner", false, "corner", 2.6, 1), ("Center", false, "center", 2.6, 1), (fillSvg, true, "fill", .9, 1), ("<path d='m 0.65,0.25 v 0.4 l -0.4,-0.2 z' />", true, "switch", .9, 2), ("Clear", false, "clear", 2.55, 2), ("Undo", false, "undo", 2.1, 2), ("Redo", false, "redo", 2.1, 2), (helpSvg, true, "help", .9, 2))); var buttonsLeft = Ut.NewArray <(string label, bool isSvg, string id, double width, int row)>(9, btn => ((btn + 1).ToString(), false, $"{btn + 1}-left", .85, 0)) .Concat(Ut.NewArray <(string label, bool isSvg, string id, double width, int row)>( ("Clear", false, "clear-left", 2.45, 1), ("Undo", false, "undo-left", 2.45, 1), ("Redo", false, "redo-left", 2.45, 1), ("<path d='m 0.25,0.25 v 0.4 l 0.4,-0.2 z' />", true, "switch-left", .9, 1))); string renderButton(string id, double x, double y, double width, string label, bool isSvg = false) => $@" <g class='button' id='{id}' transform='translate({x}, {y})'> <rect class='clickable' x='0' y='0' width='{width}' height='.9' stroke-width='.025' rx='.08' ry='.08'/> {(isSvg ? label : $"<text class='label' x='{width / 2}' y='.65' font-size='.55' text-anchor='middle'>{label}</text>")}
private HttpResponse processPost(HttpRequest req) { switch (req.Post["fnc"].Value) { case "create": var items = req.Post["items"].Value.Replace("\r", "").Trim().Split('\n').Select(line => line.Trim()).ToArray(); var hash = MD5.Create().ComputeHash(items.JoinString("\n").ToUtf8()).ToHex(); var newSet = new RankSet { Hash = hash, Items = items, Name = req.Post["title"].Value }; if (!_setsDic.ContainsKey(hash)) { lock (this) if (!_setsDic.ContainsKey(hash)) { var newSetSlim = newSet.ToSlim(); _setsDic[hash] = newSetSlim; _setsList.Add(newSetSlim); ClassifyJson.SerializeToFile(newSet, setPath(hash)); } } return(HttpResponse.Redirect(req.Url.WithQuery("set", hash))); case "start": lock (this) { var currentSetHash = req.Post["set"].Value; var setFilePath = setPath(currentSetHash); if (!File.Exists(setFilePath)) { return(HttpResponse.PlainText("That set does not exist.", HttpStatusCode._404_NotFound)); } var currentSet = ClassifyJson.DeserializeFile <RankSet>(setFilePath); retry: var publicToken = Rnd.GenerateString(32); var privateToken = Rnd.GenerateString(32); var path = rankingPath(publicToken); if (File.Exists(path) || currentSet.DicRankings.ContainsKey(publicToken)) { goto retry; } var newRanking = new RankRanking { Finished = false, PublicToken = publicToken, PrivateToken = privateToken, SetHash = currentSetHash, Title = req.Post["title"].Value, Question = req.Post["question"].Value }; var newRankingSlim = newRanking.ToSlim(); currentSet.DicRankings[publicToken] = newRankingSlim; currentSet.ListRankings.Add(newRankingSlim); ClassifyJson.SerializeToFile(newRanking, path); ClassifyJson.SerializeToFile(currentSet, setFilePath); return(HttpResponse.Redirect(req.Url.WithoutQuery().WithQuery("ranking", publicToken).WithQuery("secret", privateToken))); } case "rank": lock (this) { var publicToken = req.Post["ranking"].Value; var privateToken = req.Post["secret"].Value; var rankingFilePath = rankingPath(publicToken); if (!File.Exists(rankingFilePath)) { return(HttpResponse.PlainText("That ranking does not exist.", HttpStatusCode._404_NotFound)); } var currentRanking = ClassifyJson.DeserializeFile <RankRanking>(rankingFilePath); if (privateToken != currentRanking.PrivateToken) { return(HttpResponse.PlainText("You cannot vote in this ranking.", HttpStatusCode._404_NotFound)); } var setFilePath = setPath(currentRanking.SetHash); if (!File.Exists(setFilePath)) { return(HttpResponse.PlainText("That set does not exist.", HttpStatusCode._404_NotFound)); } var currentSet = ClassifyJson.DeserializeFile <RankSet>(setFilePath); if (!int.TryParse(req.Post["ix1"].Value, out var ix1) || !int.TryParse(req.Post["ix2"].Value, out var ix2) || !int.TryParse(req.Post["more"].Value, out var more) || (more != ix1 && more != ix2)) { return(HttpResponse.PlainText("Invalid integers.", HttpStatusCode._404_NotFound)); } var newComparison = new RankComparison(more == ix1 ? ix2 : ix1, more == ix1 ? ix1 : ix2); // Transitive closure var ancestorLesses = currentRanking.Comparisons.Where(c => c.More == newComparison.Less).Select(c => c.Less).ToList(); ancestorLesses.Add(newComparison.Less); var descendantMores = currentRanking.Comparisons.Where(c => c.Less == newComparison.More).Select(c => c.More).ToList(); descendantMores.Add(newComparison.More); for (int i = 0; i < ancestorLesses.Count; i++) { for (int j = 0; j < descendantMores.Count; j++) { currentRanking.Comparisons.Add(new RankComparison(ancestorLesses[i], descendantMores[j])); } } var result = attemptRanking(currentRanking, currentSet); if (result.ix1 == -1) { currentRanking.Finished = true; // This relies on reference equality, i.e. that currentSet.ListRankings contains the same object currentSet.DicRankings[publicToken].Finished = true; ClassifyJson.SerializeToFile(currentSet, setFilePath); } ClassifyJson.SerializeToFile(currentRanking, rankingFilePath); return(HttpResponse.Redirect(req.Url.WithoutQuery().WithQuery("ranking", publicToken).WithQuery("secret", privateToken))); } } return(HttpResponse.PlainText("What?", HttpStatusCode._500_InternalServerError)); }
/// <summary>Saves the settings stored in <see cref="Settings"/>.</summary> protected void SaveSettings() { SettingsSaver.SaveSettings(ClassifyJson.Serialize(Settings)); }
static int DoMain(string[] args) { // Parse command-line arguments Args = CommandLineParser.ParseOrWriteUsageToConsole <CmdLine>(args); if (Args == null) { return(-1); } // Load settings file if (Args.SettingsPath != null) { if (File.Exists(Args.SettingsPath)) { Settings = ClassifyJson.DeserializeFile <Settings>(Args.SettingsPath); } else { Settings = new Settings(); ClassifyJson.SerializeToFile(Settings, Args.SettingsPath); } RefreshAccessControl = Settings.SkipRefreshAccessControlDays == null || (Settings.LastRefreshAccessControl + TimeSpan.FromDays((double)Settings.SkipRefreshAccessControlDays) < DateTime.UtcNow); Console.WriteLine($"Refresh access control: {RefreshAccessControl}"); Console.WriteLine($"Update metadata: {UpdateMetadata}"); } // Initialise log files var startTime = DateTime.UtcNow; if (Args.LogPath != null) { if (Args.LogPath == "") { Args.LogPath = PathUtil.AppPath; } var enc = new UTF8Encoding(false, throwOnInvalidBytes: false); // allows us to log filenames that are not valid UTF-16 (unpaired surrogates) ActionLog = new IO.StreamWriter(IO.File.Open(Path.Combine(Args.LogPath, $"HoboMirror-Actions.{DateTime.Today:yyyy-MM-dd}.txt"), IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Read), enc); ChangeLog = new IO.StreamWriter(IO.File.Open(Path.Combine(Args.LogPath, $"HoboMirror-Changes.{DateTime.Today:yyyy-MM-dd}.txt"), IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Read), enc); ErrorLog = new IO.StreamWriter(IO.File.Open(Path.Combine(Args.LogPath, $"HoboMirror-Errors.{DateTime.Today:yyyy-MM-dd}.txt"), IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Read), enc); CriticalErrorLog = new IO.StreamWriter(IO.File.Open(Path.Combine(Args.LogPath, $"HoboMirror-ErrorsCritical.{DateTime.Today:yyyy-MM-dd}.txt"), IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Read), enc); DebugLog = new IO.StreamWriter(IO.File.Open(Path.Combine(Args.LogPath, $"HoboMirror-Debug.{DateTime.Today:yyyy-MM-dd}.txt"), IO.FileMode.Append, IO.FileAccess.Write, IO.FileShare.Read), enc); } try { // Parse volumes to be snapshotted var tasks = Args.FromPath.Zip(Args.ToPath, (from, to) => new { FromPath = from, ToPath = to, ToGuard = Path.Combine(to, "__HoboMirrorTarget__.txt"), FromVolume = Regex.Match(from, @"^\\\\\?\\Volume{[^}]+}\\").Apply(match => match.Success ? match.Groups[0].Value : null) ?? Regex.Match(from, @"^\w:\\").Apply(match => match.Success ? match.Groups[0].Value : null) ?? Ut.Throw <string>(new InvalidOperationException($"Expected absolute path: {from}")) // this should be taken care of by the CmdLine specification, so throw here }); // Log header LogAll("=============="); LogAll($"Started at {DateTime.Now}"); // Refuse to mirror without a guard file foreach (var task in tasks) { if (!File.Exists(task.ToGuard) || !File.ReadAllText(task.ToGuard).ToLower().Contains("allow")) { LogError($"Target path is not marked with a guard file: {task.ToPath}"); LogError($"Due to the potentially destructive nature of mirroring, every mirror destination must contain a guard file. This path does not. Mirroring aborted."); LogError($"To allow mirroring to this path, please create a file at {task.ToGuard}. The file must contain the word “allow”."); LogError($"Remember that HoboMirror will delete files at this path without confirmation."); return(-1); } } // Enable the necessary privilege to read and write everything try { WinAPI.ModifyPrivilege(PrivilegeName.SeBackupPrivilege, true); WinAPI.ModifyPrivilege(PrivilegeName.SeRestorePrivilege, true); //WinAPI.ModifyPrivilege(PrivilegeName.SeSecurityPrivilege, true); //WinAPI.ModifyPrivilege(PrivilegeName.SeTakeOwnershipPrivilege, true); } catch (Win32Exception e) { LogError("Unable to obtain the necessary privileges. Some files and/or attributes will not be replicated."); LogError(e.Message); } // Perform the mirroring var volumes = tasks.GroupBy(t => t.FromVolume).Select(g => g.Key).ToArray(); using (var vsc = UseVolumeShadowCopy ? new VolumeShadowCopy(volumes) : null) { var vscVolumes = UseVolumeShadowCopy ? vsc.Volumes : new ReadOnlyDictionary <string, VolumeShadowCopyVol>(volumes.ToDictionary(vol => vol, vol => new VolumeShadowCopyVol { Path = vol, SnapshotPath = vol })); foreach (var task in tasks) { var fromPath = Path.Combine(vscVolumes[task.FromVolume].SnapshotPath, task.FromPath.Substring(task.FromVolume.Length)); LogAll($" Mirror task: from “{task.FromPath}” to “{task.ToPath}” (volume snapshot path: {fromPath})"); } foreach (var ignore in Args.IgnorePath.Concat(Settings.IgnorePaths).Order()) { LogAll($" Ignore path: “{ignore}”"); } foreach (var ignore in Settings.IgnoreDirNames) { LogAll($" Ignore directory name: “{ignore}”"); } foreach (var task in tasks) { GetOriginalSrcPath = str => str.Replace(vscVolumes[task.FromVolume].SnapshotPath, task.FromVolume).Replace(@"\\", @"\"); if (!Directory.Exists(task.ToPath)) { ActCreateDirectory(task.ToPath); } var srcItem = new Item(new DirectoryInfo(Path.Combine(vscVolumes[task.FromVolume].SnapshotPath, task.FromPath.Substring(task.FromVolume.Length))), ItemType.Dir); var tgtItem = CreateItem(new DirectoryInfo(task.ToPath)); if (tgtItem != null) { SyncDir(srcItem, tgtItem); } else { LogError($"Unable to execute mirror task: {task.FromPath}"); } } } // List changed directories and update change counts LogChange("", null); LogChange("DIRECTORIES WITH AT LEAST ONE CHANGE:", null); if (Settings == null) { foreach (var chg in ChangedDirs.Order()) { LogChange(" " + chg, null); } } else { foreach (var dir in ChangedDirs) { Settings.DirectoryChangeCount[dir].TimesChanged++; } LogChange("(sorted from rarely changing to frequently changing)", null); var changes = from dir in ChangedDirs let match = Settings.GroupDirectoriesForChangeReport.Select(dg => dg.GetMatch(dir)).Where(m => m != null).MinElementOrDefault(s => s.Length) group dir by match ?? dir into grp let changeCounts = grp.Select(p => Settings.DirectoryChangeCount[p]) select new { path = grp.Key, changeFreq = changeCounts.Sum(ch => ch.TimesChanged) / (double)changeCounts.Sum(ch => ch.TimesScanned) }; foreach (var chg in changes.OrderBy(ch => ch.changeFreq)) { LogChange($" {chg.path} — {chg.changeFreq:0.0%}", null); } } if (RefreshAccessControl) { Settings.LastRefreshAccessControl = DateTime.UtcNow; } // Save settings file if (Args.SettingsPath != null) { ClassifyJson.SerializeToFile(Settings, Args.SettingsPath); } return(CriticalErrors > 0 ? 2 : Errors > 0 ? 1 : 0); } #if !DEBUG catch (Exception e) { LogCriticalError($"Unhandled exception ({e.GetType().Name}): {e.Message}"); LogCriticalError(e.StackTrace); return(1); } #endif finally { // Close log files if (Args.LogPath != null) { foreach (var log in new[] { ActionLog, ChangeLog, ErrorLog, CriticalErrorLog, DebugLog }) { log.WriteLine($"Ended at {DateTime.Now}. Time taken: {(DateTime.UtcNow - startTime).TotalMinutes:#,0.0} minutes"); log.Dispose(); } } } }
public override void Init(LoggerBase log) { base.Init(log); if (Settings.DataDir != null && Directory.Exists(Settings.DataDir)) { _setsList.Clear(); _setsList.AddRange(new DirectoryInfo(Settings.DataDir).EnumerateFiles("set-*.json").Select(f => ClassifyJson.DeserializeFile <RankSet>(f.FullName).ToSlim())); _setsDic = _setsList.ToDictionary(set => set.Hash); } }