Пример #1
0
        /// <summary>
        /// When the initial report on a bill is generated, it is in so-called "canonical" form, meaning that
        /// no position is taken, there is no meaningful summary, and so on.  In essence, a blank form is generated.
        /// The user is then given the opportunity to edit the blank form, turning it into an actual report on the bill.
        /// Once that editing is complete, the database BillRow table is updated with the position given in the
        /// edited bill report.
        /// </summary>
        /// <param name="measure"></param>
        public static void GenerateCanonicalReport(string measure)
        {
            // Generate the canonical bill
            BillRow       row      = BillRow.Row(BillUtils.Ensure4DigitNumber(measure));
            List <string> contents = BaseReportContents(row, string.Empty);
            string        path     = $"{Path.Combine(Config.Instance.HtmlFolder, BillUtils.EnsureNoLeadingZerosBill(measure))}.html";

            WriteTextFile(contents, path);
            // Let the user edit the canonical bill, which has been written to disk.
            var process = Process.Start("notepad.exe", path);

            if (process != null)
            {
                process.WaitForExit();
            }
            else
            {
                LogAndShow($"CreateNewReports.GenerateCanonicalReport: Failed to start Notepad for {path}.");
            }
            // Update the database position
            BillRow.UpdatePosition(BillUtils.Ensure4DigitNumber(measure), "");
            GetPositionAndSummary(path, out List <string> summary, out List <string> position_list);
            string first_line = position_list.FirstOrDefault();
            string position   = first_line != null?Regex.Replace(first_line, ".*?:(.*)", "$1") : "None Specified";

            BillRow.UpdatePosition(measure, position.Trim());
            LogAndShow($"Completed {path}");
        }
Пример #2
0
        public void Run(Form1 form1)
        {
            var start_time = DateTime.Now;

            try {
                LogAndDisplay(form1.txtImportProgress, "Determining most recent version of each bill.");
                // Need to know which lob file describes the most recent version of each bill
                GlobalData.MostRecentEachBill = MostRecentBills.Identify(Config.Instance.BillsFolder);

                // Need to copy GlobalData.Profiles to a temporary, update the temporary, then copy the temporary back.
                // BillRanker.Compute(ref GlobalData.Profiles) results in
                //    A property or indexer may not be passed as an out or ref parameter.
                //    Property "Profiles" access returns temporary value.
                //    "ref" argument must be an assignable value, field or array element.
                LogAndDisplay(form1.txtImportProgress, "Computing positive and negative scores for all bills.");
                List <BillProfile> mr_profiles = Profiles(GlobalData.MostRecentEachBill); // Prepare for ranking the bills
                BillRanker.Compute(ref mr_profiles);
                GlobalData.Profiles = mr_profiles;

                LogAndDisplay(form1.txtImportProgress, "Initializing BillRows from BILL_TBL.dat and elsewhere.");
                string path = $"{Path.Combine(Config.Instance.BillsFolder, "BILL_TBL.dat")}";
                BuildBillRowsTable(form1, path, GlobalData.Profiles);
                // Update the database BillRows table
                LogAndDisplay(form1.txtImportProgress, "Clearing and rewriting BillRows in the database.");
                BillRow.WriteRowset(GlobalData.BillRows);
            } catch (Exception ex) {
                LogAndThrow($"InitializeBillRows.Run: {ex.Message}.");
            }
            var elapsed = DateTime.Now - start_time;

            LogAndDisplay(form1.txtImportProgress, $"BillRows initialization complete. Elapsed Time: {elapsed.ToString("c")} ");
        }
Пример #3
0
        private static MatchCollection SingleFile(BillRow row, Regex rx)
        {
            var result   = string.Empty;
            var contents = ContentsWithHTML(row);
            var trimmed  = RemoveHTML(contents);

            //File.WriteAllText("C:/Scratch/temp.txt", contents);
            return(rx.Matches(trimmed));
        }
Пример #4
0
        /// <summary>
        /// Bills with no recorded positions are considered bills with no reports.
        /// </summary>
        /// <returns></returns>
        private IOrderedEnumerable <BillRow> CollectNoPositionBills()
        {
            var all_bills = BillRow.RowSet(); // All bills for the current biennium.
            var result    =
                from item in all_bills
                where ((item.Position == string.Empty) && (item.NegativeScore > 0))
                orderby item.NegativeScore descending select item;

            return(result);
        }
Пример #5
0
        private DateTime DateFromHistoryTable(string path)
        {
            string                bill     = Path.GetFileNameWithoutExtension(path);
            BillRow               row      = BillRow.Row(BillUtils.Ensure4DigitNumber(bill));
            string                name_ext = Path.GetFileName(row.Lob); // BillVersionTable bill_xml is unique
            BillVersionRow        bv_row   = GlobalData.VersionTable.Scalar(name_ext);
            List <BillHistoryRow> history  = GlobalData.HistoryTable.RowSet(bv_row.BillID);

            DateTime.TryParse(history.First().ActionDate, out DateTime date_result);
            return(date_result);
        }
Пример #6
0
 private void Create(string bill, string path)
 {
     try {
         BillRow       row      = BillRow.Row(BillUtils.Ensure4DigitNumber(bill));
         List <string> contents = CreateIndividualReport.ReportContents(row, path);
         WriteTextFile(contents, path);
         var message = $"Regenerated {row.Bill} report.";
         BaseController.LogThis(message);
     } catch (Exception ex) {
         LogAndThrow($"IndividualReport.Create: {ex.Message}.");
     }
 }
Пример #7
0
        /// <summary>
        /// This method reads a raw bill file, trims various introductory information, and returns the actual bill text.
        /// It does not remove HTML control information embedded in that text.
        /// </summary>
        /// <param name="row"></param>
        /// <returns></returns>
        private static string ContentsWithHTML(BillRow row)
        {
            var contents = string.Empty;

            if (File.Exists(row.Lob))
            {
                contents = FileUtils.FileContents(row.Lob);
                var end_marker = "</caml:Preamble>";
                var offset     = contents.IndexOf(end_marker) + end_marker.Length;
                contents = contents.Substring(offset);
            }
            return(contents);
        }
Пример #8
0
        /// <summary>
        /// Sets contents of some database tables, using data from table files contained in the zipped download.
        /// </summary>
        /// <returns>True if all is well, False if unable to access table files expected to be present</returns>
        public static bool EnsureGlobalData()   // public so that XUnit TestNewOrChangePrefix can ensure table contents are available
        {
            bool result = true;

            Config.Instance.ReadYourself(); // Start of configuration data lifetime
            GlobalData.Profiles = new List <BillProfile>();
            if (GlobalData.BillRows == null)
            {
                GlobalData.BillRows = BillRow.RowSet();
            }
            if (GlobalData.HistoryTable == null)
            {
                string path = Path.Combine(Config.Instance.BillsFolder, "BILL_HISTORY_TBL.dat");
                if (File.Exists(path))
                {
                    GlobalData.HistoryTable = new BillHistoryTable(path);
                }
                else
                {
                    result = false;
                }
            }
            if (GlobalData.VersionTable == null)
            {
                string path = Path.Combine(Config.Instance.BillsFolder, "BILL_VERSION_TBL.dat");
                if (File.Exists(path))
                {
                    GlobalData.VersionTable = new BillVersionTable(path);
                }
                else
                {
                    result = false;
                }
            }
            if (GlobalData.LocationTable == null)
            {
                string path = Path.Combine(Config.Instance.BillsFolder, "LOCATION_CODE_TBL.dat");
                if (File.Exists(path))
                {
                    GlobalData.LocationTable = new LocationCodeTable(path);
                }
                else
                {
                    result = false;
                }
            }
            GlobalData.MostRecentEachBill = new List <Bill_Identifier>();
            return(result);
        }
Пример #9
0
        /// <summary>
        /// Allow the user to update the current bill report.
        /// If the position is changed, the database BillRow table is updated with the new position.
        /// </summary>
        /// <param name="measure"></param>
        private void UpdateReport(string measure)
        {
            string path    = $"{Path.Combine(Config.Instance.HtmlFolder, BillUtils.EnsureNoLeadingZerosBill(measure))}.html";
            var    process = Process.Start("notepad.exe", path);

            if (process != null)
            {
                process.WaitForExit();
            }
            else
            {
                BaseController.LogAndShow($"CreateNewReports.GenerateCanonicalReport: Failed to start Notepad for {path}.");
            }
            // Update the database position
            BillRow.UpdatePosition(BillUtils.Ensure4DigitNumber(measure), "");
            BaseController.LogAndShow($"Update for {path} is complete.");
        }
Пример #10
0
        /// <summary>
        /// Before a bill is chaptered, the last action is the .First() line in the history.
        /// When a bill is chaptered, its history may not end with the "Chaptered by Secretary of State" because
        /// usually multiple actions take place on the same day.  Therefore, for a chaptered bill, report
        /// the line containing "Chaptered by Secretary of State".  It may not be the first line in the history.
        /// </summary>
        /// <param name="row">BillRow describing the bill being processed</param>
        /// <returns></returns>
        protected static string FindLastAction(BillRow row)
        {
            string                name_ext = Path.GetFileName(row.Lob); // BillVersionTable bill_xml is unique
            BillVersionRow        bv_row   = GlobalData.VersionTable.Scalar(name_ext);
            List <BillHistoryRow> history  = GlobalData.HistoryTable.RowSet(bv_row.BillID);

            string result = "Could not find last action.";

            if (row.MeasureState != "Chaptered")
            {
                result = history.First().Action;
            }
            else
            {
                var want_this = history.Find(x => x.Action.Contains("Chaptered by Secretary of State"));
                result = want_this.Action;
            }
            return(result);
        }
Пример #11
0
        /// <summary>
        /// Build GlobalData.BillRows from scratch.
        /// The steps for this are
        ///   1. Import the BILL_TBL.dat data into GlobalData.BillRows.  This fills 9 of the 15 BillRow fields.
        /// </summary>
        /// <param name="form1">The main (Form1) display form. Display messages and progress here.</param>
        /// <param name="path_bill_tbl">Fully-qualified path to BILL_TBL.dat</param>
        /// <param name="mostRecentEachBill">For each bill, the Bill_Identifier identifying the most recent version</param>
        private void BuildBillRowsTable(Form1 form1, string path_bill_tbl, List <BillProfile> profiles)
        {
            // Import BILL_TBL.dat, which is the legislative site's information on bills before the legislature.
            // Trim all non-bill items during the import -- we want only type AB and SB (Assembly and Senate Bills)
            var bill_table_wrapper = new Bill_Tbl_Wrapper();

            bill_table_wrapper.ReadYourself();  // Import BILL_TBL.dat
            List <BillRow> rows_with_positions = BillRow.RowsetByQuery("Select * from Billrows Where Position <> ''");
            List <BillRow> all_rows            = BillRow.RowSet();
            List <string>  reports             = BillUtils.HtmlFolderContents();

            // Re-create GlobalData.BillRows, using data from bill_table_wrapper and elsewhere.
            GlobalData.BillRows.Clear();
            List <string> SkipIf = new List <String>()
            {
                "Chaptered", "Died", "Enrolled", "Failed", "Failed Passage in Committee", "Vetoed"
            };

            foreach (var item in bill_table_wrapper)
            {
                // Don't process bills that will not progress further in the legislature.
                //string result = SkipIf.FirstOrDefault(s => s == item.Current_status);
                //if (result != null) continue;

                // Use data from bill_table_wrapper.  Some fields are left blank.
                var bill_row = new BillRow();
                bill_row.MeasureType = item.Measure_type;                                                                  // e.g. AB
                bill_row.MeasureNum  = BillUtils.Ensure4DigitNumber(item.Measure_num);                                     // e.g. 0010
                bill_row.Bill        = $"{bill_row.MeasureType}{BillUtils.EnsureNoLeadingZerosBill(bill_row.MeasureNum)}"; // e.g. AB10
                //bill_row.Lob           = item.;
                //bill_row.NegativeScore = item;
                //bill_row.PositiveScore = item;
                //bill_row.Position      = item; // Hand Entered, e.g. Monitor
                bill_row.BillVersionID = VersionID(item); // e.g. 20190AB199INT
                //bill_row.Author        = item;
                //bill_row.Title         = item;
                bill_row.Location      = item.Current_location;      // e.g. CX08
                bill_row.Location2nd   = item.Current_secondary_loc; // e.g. Committee
                bill_row.MeasureState  = item.Measure_state;         // e.g. Amended Assembly
                bill_row.CurrentHouse  = item.Current_house;         // e.g. Assembly
                bill_row.CurrentStatus = item.Current_status;        // e.g. In Committee Process

                // Obtain the author, title, lob file path, and positive/negative scores from the profile for this bill
                var four_digit_billid = BillUtils.Ensure4DigitNumber(bill_row.Bill);
                var profile           = (from x in profiles where x.Identifier.BillID == four_digit_billid select x).First();
                if (profile != null)
                {
                    bill_row.Author        = profile.Identifier.Author;
                    bill_row.Title         = profile.Identifier.Title;
                    bill_row.Lob           = profile.Identifier.LobPath;
                    bill_row.NegativeScore = profile.NegScore;
                    bill_row.PositiveScore = profile.PosScore;
                }
                else
                {
                    throw new ApplicationException($"InitializeBillRows.BuildBillRowsTable: Bill {bill_row.Bill} is present in bill_table_wrapper, but not in GlobalData.Profiles.");
                }

                // Fill in the Position data -- the position we are taking on this bill.  If we have a position, it is
                // in one of two places
                //    1.  The database BillRows table (not all bills have a report), or
                var pos = (from x in rows_with_positions where x.Bill == bill_row.Bill select x).FirstOrDefault();
                if (pos != null)
                {
                    bill_row.Position = pos.Position;
                }
                //    2.  If that table hasn't been updated, in the actual report
                // If the two are in conflict, the bill report wins.
                var short_id    = BillUtils.EnsureNoLeadingZerosBill(bill_row.Bill);
                var report_file = (from x in reports where x.Contains($"{short_id}.html") select x).FirstOrDefault();
                if (report_file != null)
                {
                    var report = new BillReport(report_file);
                    bill_row.Position = report.Position;
                }
                // Add this row to GlobalData.BillRows
                GlobalData.BillRows.Add(bill_row);
            }

            // Sort the table before returning it.  Ordered by bill ID, e.g. AB0001, communicates well
            GlobalData.BillRows = GlobalData.BillRows.OrderBy(a => a.Bill).ToList();
        }
Пример #12
0
        /// <summary>
        /// Generate a new individual report or re-generate one that already exists.
        /// </summary>
        /// <param name="row">Bill description from the BillRows table</param>
        /// <param name="path">Path to the .lob file for the bill's current version</param>
        /// <returns></returns>
        protected static List <string> BaseReportContents(BillRow row, string path)
        {
            string                name_ext = Path.GetFileName(row.Lob); // BillVersionTable bill_xml is unique
            BillVersionRow        bv_row   = GlobalData.VersionTable.Scalar(name_ext);
            List <BillHistoryRow> history  = GlobalData.HistoryTable.RowSet(bv_row.BillID);
            var location_code     = history.First().TernaryLocation;
            var location_code_row = GlobalData.LocationTable.Scalar(location_code);

            string appropriation = bv_row.Appropriation;
            string author        = row.Author;
            string bill_id       = row.Bill;
            string fiscal        = bv_row.FiscalCommittee;
            string house         = history.First().PrimaryLocation;
            string last_action   = FindLastAction(row);
            string location      = location_code_row == null?BillUtils.WhenNullLocationCode(history) : location_code_row.Description;

            string local_pgm  = bv_row.LocalProgram;
            string number     = row.MeasureNum.TrimStart('0');
            string title      = row.Title;
            string type_house = $"{bill_id.First()}B";
            string vers_id    = row.BillVersionID;
            string vote       = bv_row.VoteRequired;

            // Position and Summary data come from the previous version of the bill report
            // If the passed path is null or empty, then this method was called when no previous report exists.
            // When regenerating a report, there is a previous report.
            var summary      = new List <string>();
            var position     = new List <string>();
            var shortsummary = string.Empty;
            var committees   = string.Empty;
            var likelihood   = string.Empty;

            if (CommonUtils.IsNullOrEmptyOrWhiteSpace(path))
            {
                // do nothing
            }
            else
            {
                summary      = PreviousReport.Summary(path);
                position     = PreviousReport.Position(path);
                shortsummary = PreviousReport.ShortSummary(path);
                committees   = PreviousReport.Committees(path);
                likelihood   = PreviousReport.Likelihood(path);
            }

            // With all necessary data obtained, generate the report file.
            // Both initial creation (new bill) and re-generation are processed here.
            List <string> result = BeginIndividualReport(type_house, number, author, title);

            // Review
            result.AddRange(ReportReview(summary));
            // Position
            result.AddRange(ReportPosition(position));
            // Short Summary, Committees Prediction and Passage Likelihood
            result.AddRange(ReportSummaryPredictLikelihood(shortsummary, committees, likelihood));
            // Status, Location, etc
            result.AddRange(ReportStatusLocationEtc(location, last_action, vote, appropriation, fiscal, local_pgm, history));
            // Bill History
            result.AddRange(ReportHistory(history));
            return(result);
        }
Пример #13
0
        /// <summary>
        /// This background worker performs the time-consuming task of searching all current bills for two words
        /// that are within a specified distance of each other.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void bgw_SearchNearby(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            if (worker == null)
            {
                throw new ApplicationException("bgw_SearchNearby failed to instantiate BackgroundWorker");
            }

            // Ensure passed strings are valid.  If so take the minimum and maximum distance values.
            string             param_problem = "";
            bgwSearchArguments args          = (bgwSearchArguments)e.Argument;

            if (NEWs(args.word1))
            {
                BuildErrorMessage(ref param_problem, "First word must be specified");
            }
            if (NEWs(args.word2))
            {
                BuildErrorMessage(ref param_problem, "Second word must be specified");
            }
            if (NEWs(args.text_min))
            {
                BuildErrorMessage(ref param_problem, "Minimum size must be specified");
            }
            if (NEWs(args.text_max))
            {
                BuildErrorMessage(ref param_problem, "Maximum must be specified");
            }
            args.word1    = args.word1.Trim(); args.word2 = args.word2.Trim();
            args.text_min = args.text_min.Trim(); args.text_max = args.text_max.Trim();
            if (!Int16.TryParse(args.text_min, out short min_dist))
            {
                BuildErrorMessage(ref param_problem, "Unable to parse Minimum distance");
            }
            if (min_dist < 0)
            {
                BuildErrorMessage(ref param_problem, "Minimum distance must not be negative");
            }
            if (!Int16.TryParse(args.text_max, out short max_dist))
            {
                BuildErrorMessage(ref param_problem, "Unable to parse Maximum distance");
            }
            if (max_dist < 0)
            {
                BuildErrorMessage(ref param_problem, "Maximum distance must not be negative");
            }
            if (param_problem.Length > 0)
            {
                throw new ApplicationException(param_problem);
            }

            GlobalData.BillRows = BillRow.RowSet();
            var bills       = GlobalData.BillRows.OrderBy(item => item.Bill).ToList();
            int one_percent = bills.Count / 100;

            Regex rx = CreateRegex(args.word1, args.word2, min_dist, max_dist);
            int   progress_bar_value = 0;

            using (StreamWriter sw_matches = new StreamWriter("C:/Scratch/Scout2_Matches.txt")) {
                using (StreamWriter sw_bills = new StreamWriter("C:/Scratch/Scout2_Bills.txt")) {
                    int count = 0;
                    foreach (var bill in bills)
                    {
                        MatchCollection found = SingleFile(bill, rx);
                        if (found.Count > 0)
                        {
                            sw_bills.WriteLine($"{bill.Bill}");
                            sw_matches.WriteLine($"{bill.Bill} {bill.Title} ({bill.Author})");
                            foreach (var match in found)
                            {
                                sw_matches.WriteLine($"\t{Cleanup(match.ToString())}");
                            }
                        }
                        if (++count % one_percent == 0)
                        {
                            worker.ReportProgress(++progress_bar_value);
                        }
                    }
                }
            }
        }
Пример #14
0
 // Create a Bill Report, given a bill identifier such as AB12
 public static List <string> ReportContents(BillRow row, string path)
 {
     return(BaseReportContents(row, path));
 }