Exemplo n.º 1
0
        //Try to find a missing image
        private async void MenuItemEditFindMissingImage_Click(object sender, RoutedEventArgs e)
        {
            // Don't do anything if the image actually exists. This should not fire, as this menu item is only enabled
            // if there is a current image that doesn't exist. But just in case...
            if (null == this.DataHandler?.ImageCache?.Current || File.Exists(FilesFolders.GetFullPath(this.DataHandler.FileDatabase.FolderPath, this.DataHandler?.ImageCache?.Current)))
            {
                return;
            }

            string   folderPath   = this.DataHandler.FileDatabase.FolderPath;
            ImageRow currentImage = this.DataHandler?.ImageCache?.Current;

            // Search for - and return as list of relative path / filename tuples -  all folders under the root folder for files with the same name as the fileName.
            List <Tuple <string, string> > matchingRelativePathFileNameList = Util.FilesFolders.SearchForFoldersContainingFileName(folderPath, currentImage.File);

            // Remove any of the tuples that are spoken for i.e., that are already associated with a row in the database
            for (int i = matchingRelativePathFileNameList.Count - 1; i >= 0; i--)
            {
                if (this.DataHandler.FileDatabase.ExistsRelativePathAndFileInDataTable(matchingRelativePathFileNameList[i].Item1, matchingRelativePathFileNameList[i].Item2))
                {
                    // We only want matching files that are not already assigned to another datafield in the database
                    matchingRelativePathFileNameList.RemoveAt(i);
                }
            }

            // If there are no remaining tuples, it means no potential matches were found.
            // Display a message saying so and abort.
            if (matchingRelativePathFileNameList.Count == 0)
            {
                Dialogs.MissingFileSearchNoMatchesFoundDialog(this, currentImage.File);
                return;
            }

            // Now retrieve a list of all filenames located in the same folder (i.e., that have the same relative path) as the missing file.
            List <string> otherMissingFiles = this.DataHandler.FileDatabase.SelectFileNamesWithRelativePathFromDatabase(currentImage.RelativePath);

            // Remove the current missing file from the list, as well as any file that exists i.e., that is not missing.
            for (int i = otherMissingFiles.Count - 1; i >= 0; i--)
            {
                if (String.Equals(otherMissingFiles[i], currentImage.File) || File.Exists(Path.Combine(folderPath, currentImage.RelativePath, otherMissingFiles[i])))
                {
                    otherMissingFiles.RemoveAt(i);
                }
            }

            // For those that are left (if any), see if other files in the returned locations are in each path. Get their count, save them, and pass the count as a parameter e.g., a Dict with matching files, etc.
            // Or perhapse even better, a list of file names for each path Dict<string, List<string>>
            // As we did above, go through the other missing files and remove those that are spoken for i.e., that are already associated with a row in the database.
            // What remains will be a list of  root paths, each with a list of  missing (unassociated) files that could be candidates for locating
            Dictionary <string, List <string> > otherMissingFileCandidates = new Dictionary <string, List <string> >();

            foreach (Tuple <string, string> matchingPath in matchingRelativePathFileNameList)
            {
                List <string> orphanMissingFiles = new List <string>();
                foreach (string otherMissingFile in otherMissingFiles)
                {
                    // Its a potential candidate if its not already referenced but it exists in that relative path folder
                    if (false == this.DataHandler.FileDatabase.ExistsRelativePathAndFileInDataTable(matchingPath.Item1, otherMissingFile) &&
                        File.Exists(FilesFolders.GetFullPath(FolderPath, matchingPath.Item1, otherMissingFile)))
                    {
                        orphanMissingFiles.Add(otherMissingFile);
                    }
                }
                otherMissingFileCandidates.Add(matchingPath.Item1, orphanMissingFiles);
            }

            Dialog.MissingImageLocateRelativePaths dialog = new Dialog.MissingImageLocateRelativePaths(this, this.DataHandler.FileDatabase, currentImage.RelativePath, currentImage.File, otherMissingFileCandidates);

            bool?result = dialog.ShowDialog();

            if (result == true)
            {
                Tuple <string, string> locatedMissingFile = dialog.LocatedMissingFile;
                if (String.IsNullOrEmpty(locatedMissingFile.Item2))
                {
                    return;
                }

                // Update the original missing file
                List <ColumnTuplesWithWhere> columnTuplesWithWhereList = new List <ColumnTuplesWithWhere>();
                ColumnTuplesWithWhere        columnTuplesWithWhere     = new ColumnTuplesWithWhere();
                columnTuplesWithWhere.Columns.Add(new ColumnTuple(Constant.DatabaseColumn.RelativePath, locatedMissingFile.Item1)); // The new relative path
                columnTuplesWithWhere.SetWhere(currentImage.RelativePath, currentImage.File);                                       // Where the original relative path/file terms are met
                columnTuplesWithWhereList.Add(columnTuplesWithWhere);

                // Update the other missing files in the database, if any
                foreach (string otherMissingFileName in otherMissingFileCandidates[locatedMissingFile.Item1])
                {
                    columnTuplesWithWhere = new ColumnTuplesWithWhere();
                    columnTuplesWithWhere.Columns.Add(new ColumnTuple(Constant.DatabaseColumn.RelativePath, locatedMissingFile.Item1)); // The new value
                    columnTuplesWithWhere.SetWhere(currentImage.RelativePath, otherMissingFileName);                                    // Where the original relative path/file terms are met
                    columnTuplesWithWhereList.Add(columnTuplesWithWhere);
                }

                // Now update the database
                this.DataHandler.FileDatabase.UpdateFiles(columnTuplesWithWhereList);
                await this.FilesSelectAndShowAsync().ConfigureAwait(true);
            }
        }
Exemplo n.º 2
0
        // Reads all the data from the old ImageData.xml files into the imageData structure from the XML file in the filepath.
        // Note that we need to know the code controls,as we have to associate any points read in with a particular counter control
        public static Tuple <int, int> Read(string filePath, FileDatabase imageDatabase)
        {
            // XML Preparation - follows CA3075  pattern for loading
            XmlDocument xmlDoc = new XmlDocument()
            {
                XmlResolver = null
            };

            System.IO.StringReader sreader = new System.IO.StringReader(File.ReadAllText(filePath));
            XmlReader reader = XmlReader.Create(sreader, new XmlReaderSettings()
            {
                XmlResolver = null
            });

            xmlDoc.Load(reader);

            // Import the old log (if any)
            XmlNodeList logNodes = xmlDoc.SelectNodes(Constant.ImageXml.Images + Constant.ImageXml.Slash + Constant.DatabaseColumn.Log);

            if (logNodes.Count > 0)
            {
                XmlNode logNode = logNodes[0];
                imageDatabase.ImageSet.Log = logNode.InnerText;
                imageDatabase.UpdateSyncImageSetToDatabase();
            }

            // Create three lists, each one representing the datalabels (in order found in the template) of notes, counters and choices
            // We will use these to find the matching ones in the xml data table.
            List <string> noteControlNames    = new List <string>();
            List <string> counterControlNames = new List <string>();
            List <string> choiceControlNames  = new List <string>();

            foreach (ControlRow control in imageDatabase.Controls)
            {
                // Note that code should be modified to  deal with flag controls
                switch (control.Type)
                {
                case Constant.Control.Counter:
                    counterControlNames.Add(control.DataLabel);
                    break;

                case Constant.Control.FixedChoice:
                    choiceControlNames.Add(control.DataLabel);
                    break;

                case Constant.Control.Note:
                    noteControlNames.Add(control.DataLabel);
                    break;

                default:

                    break;
                }
            }

            XmlNodeList  nodeList         = xmlDoc.SelectNodes(Constant.ImageXml.Images + Constant.ImageXml.Slash + Constant.DatabaseColumn.Image);
            int          imageID          = 0;
            TimeZoneInfo imageSetTimeZone = imageDatabase.ImageSet.GetSystemTimeZone();
            List <ColumnTuplesWithWhere> imagesToUpdate  = new List <ColumnTuplesWithWhere>();
            List <ColumnTuplesWithWhere> markersToUpdate = new List <ColumnTuplesWithWhere>();
            List <Tuple <string, List <ColumnTuple> > > fileNamesMarkersList = new List <Tuple <string, List <ColumnTuple> > >();

            int successCounter = 0;
            int skippedCounter = 0;

            foreach (XmlNode node in nodeList)
            {
                imageID++;

                // We ignore:
                // - Folder and Relative path, as the new template will have the correct values
                // - ImageQuality, as the new Timelapse version probably has a better determination of it
                // - DeleteFlag, as the old-style xml templates didn't have them
                // - Flags as the old-style xml templates didn't have them

                List <ColumnTuple> columnsToUpdate = new List <ColumnTuple>(); // Populate the data
                // File Field - We use the file name as a key into a particular database row. We don't change the database field as it is our key.
                string imageFileName = node[Constant.ImageXml.File].InnerText;

                // If the Folder Path differs from where we had previously loaded it,
                // warn the user that the new path will be substituted in its place
                // This gets the folderName in the Xml file, but we still ahve to get the folderName as it currently exists.
                // string folderName = node[Constant.ImageXml.Folder].InnerText;

                // Date - We use the original date, as the analyst may have adjusted them
                string date = node[Constant.ImageXml.Date].InnerText;
                columnsToUpdate.Add(new ColumnTuple(Constant.DatabaseColumn.Date, date));

                // Date - We use the original time, although its almost certainly identical
                string time = node[Constant.ImageXml.Time].InnerText;
                columnsToUpdate.Add(new ColumnTuple(Constant.DatabaseColumn.Time, time));

                // DateTime
                if (DateTimeHandler.TryParseLegacyDateTime(date, time, imageSetTimeZone, out DateTimeOffset dateTime))
                {
                    columnsToUpdate.Add(new ColumnTuple(Constant.DatabaseColumn.DateTime, dateTime.UtcDateTime));
                    columnsToUpdate.Add(new ColumnTuple(Constant.DatabaseColumn.UtcOffset, dateTime.Offset));
                }

                // Notes: Iterate through
                int         innerNodeIndex = 0;
                XmlNodeList innerNodeList  = node.SelectNodes(Constant.Control.Note);
                foreach (XmlNode innerNode in innerNodeList)
                {
                    // System.Diagnostics.Debug.Print("Note: " + noteControlNames[innerNodeIndex] + " | " + innerNode.InnerText);
                    columnsToUpdate.Add(new ColumnTuple(noteControlNames[innerNodeIndex++], innerNode.InnerText));
                }

                // Choices: Iterate through
                innerNodeIndex = 0;
                innerNodeList  = node.SelectNodes(Constant.Control.FixedChoice);
                foreach (XmlNode innerNode in innerNodeList)
                {
                    // System.Diagnostics.Debug.Print("Choice: " + choiceControlNames[innerNodeIndex] + " | " + innerNode.InnerText);
                    columnsToUpdate.Add(new ColumnTuple(choiceControlNames[innerNodeIndex++], innerNode.InnerText));
                }

                // Counters: Iterate through
                List <ColumnTuple> counterCoordinates = new List <ColumnTuple>();
                innerNodeIndex = 0;
                innerNodeList  = node.SelectNodes(Constant.Control.Counter);
                foreach (XmlNode innerNode in innerNodeList)
                {
                    // Add the value of each counter to the dataline
                    XmlNodeList dataNode = innerNode.SelectNodes(Constant.DatabaseColumn.Data);
                    // System.Diagnostics.Debug.Print("Counter: " + counterControlNames[innerNodeIndex] + " | " + dataNode[0].InnerText);
                    columnsToUpdate.Add(new ColumnTuple(counterControlNames[innerNodeIndex], dataNode[0].InnerText));

                    // For each counter, find the points associated with it and compose them together as x1,y1|x2,y2|...|xn,yn
                    XmlNodeList pointNodeList = innerNode.SelectNodes(Constant.DatabaseColumn.Point);
                    string      countercoord  = String.Empty;
                    foreach (XmlNode pointNode in pointNodeList)
                    {
                        String x = pointNode.SelectSingleNode(Constant.DatabaseColumn.X).InnerText;
                        if (x.Length > 5)
                        {
                            x = x.Substring(0, 5);
                        }
                        String y = pointNode.SelectSingleNode(Constant.DatabaseColumn.Y).InnerText;
                        if (y.Length > 5)
                        {
                            y = y.Substring(0, 5);
                        }
                        countercoord += x + "," + y + "|";
                    }

                    // Remove the last "|" from the point list
                    if (!String.IsNullOrEmpty(countercoord))
                    {
                        countercoord = countercoord.Remove(countercoord.Length - 1); // Remove the last "|"
                    }

                    // Countercoords will have a list of points (possibly empty) with each list entry representing a control
                    counterCoordinates.Add(new ColumnTuple(counterControlNames[innerNodeIndex], countercoord));
                    innerNodeIndex++;
                }

                // add this image's updates to the update lists
                ColumnTuplesWithWhere imageToUpdate = new ColumnTuplesWithWhere(columnsToUpdate);
                // Since Timelapse1 didn't have relative paths, we only need to set Where using the image filename
                // imageToUpdate.SetWhere(currentFolderName, null, imageFileName); //<- replaced by the simpler SetWhere form below
                if (File.Exists(Path.Combine(Path.GetDirectoryName(filePath), imageFileName)))
                {
                    imageToUpdate.SetWhere(imageFileName);
                    imagesToUpdate.Add(imageToUpdate);
                    ColumnTuple ColumnTupleFileName = new ColumnTuple(Constant.DatabaseColumn.File, imageFileName);

                    // We have to do the markers later, as we need to get the ID of the matching filename from the data table,
                    // and use that to set the markers.
                    Tuple <string, List <ColumnTuple> > filenameMarkerTuple = new Tuple <string, List <ColumnTuple> >(imageFileName, counterCoordinates);
                    fileNamesMarkersList.Add(filenameMarkerTuple);
                    successCounter++;
                }
                else
                {
                    skippedCounter++;
                }
            }

            // batch update the data table
            imageDatabase.UpdateFiles(imagesToUpdate);

            // Now that we have updated the data table, we can update the markers.
            // We retrieve the ID of the filename associated with the markers from the data table,
            // and use that to set the correct row in the marker table.
            foreach (Tuple <string, List <ColumnTuple> > tuple in fileNamesMarkersList)
            {
                long id = imageDatabase.GetIDFromDataTableByFileName(tuple.Item1);
                ColumnTuplesWithWhere markerToUpdate = new ColumnTuplesWithWhere(tuple.Item2, id);
                markersToUpdate.Add(markerToUpdate);
                imageDatabase.UpdateMarkers(markersToUpdate);
            }

            if (reader != null)
            {
                reader.Dispose();
            }
            return(new Tuple <int, int>(successCounter, skippedCounter));
        }