Saturday, February 4, 2017

Refactoring challenge - yahtzee

Refactoring challenge
http://jonjagger.blogspot.co.uk/2012/05/yahtzee-cyber-dojo-refactoring-in-java.html

My end results

                                           Original           Refactored
Maintainablility                  61                        90
Cyclometric Complexity    64                        47
Lines of Code                     157                      86

Class Coupling                    0                         11

My refactored C# version of this


using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace RefactoringPractice { public enum DieValue { One =1, Two=2, Three=3, Four=4, Five=5, Six=6 } public class Dice { private static int NUM_DIE = 5; private int[] dice = new int[NUM_DIE]; private static DieValue[] dieValues = (DieValue[])Enum.GetValues(typeof(DieValue)); public Dice(int die1, int die2, int die3, int die4, int die5) { dice[0] = die1; dice[1] = die2; dice[2] = die3; dice[3] = die4; dice[4] = die5; } internal int Sum() { return dice.Sum(); } internal int SumOfNum(DieValue val) { return whereVal(val).Sum(); } private IEnumerable<int> whereVal(DieValue val) { return dice.Where(e => e == (int)val); } internal int CountOfNum(DieValue val) { return whereVal(val).Count(); } internal bool AllDieHaveNum(DieValue val) { return CountOfNum(val) == NUM_DIE; } internal static DieValue[] DieValues { get { return dieValues; } } } public static class Yatzy { public static int Chance(Dice dice) { return dice.Sum(); } public static int yatzy(Dice dice) { foreach(DieValue val in Dice.DieValues) { if (dice.AllDieHaveNum(val)) return 50; } return 0; } public static int Ones(Dice dice) { return dice.SumOfNum(DieValue.One); } public static int Twos(Dice dice) { return dice.SumOfNum(DieValue.Two); } public static int Threes(Dice dice) { return dice.SumOfNum(DieValue.Three); } public static int Fours(Dice dice) { return dice.SumOfNum(DieValue.Four); } public static int Fives(Dice dice) { return dice.SumOfNum(DieValue.Five); } public static int Sixes(Dice dice) { return dice.SumOfNum(DieValue.Six); } public static int ScorePair(Dice dice) { foreach(DieValue val in Dice.DieValues.Reverse()) { if (dice.CountOfNum(val) == 2) return (int)val * 2; } return 0; } public static int TwoPair(Dice dice) { int numPairs = 0; int score = 0; foreach(DieValue val in Dice.DieValues.Reverse()) { if(dice.CountOfNum(val) >= 2) { numPairs++; score += ((int)val * 2); } } if (numPairs == 2) return score; else return 0; } public static int FourOfAKind(Dice dice) { return OfAKind(4, dice); } private static int OfAKind(int num, Dice dice) { foreach (DieValue val in Dice.DieValues) { if (dice.CountOfNum(val) >= num) return (int)val * num; } return 0; } public static int ThreeOfAKind(Dice dice) { return OfAKind(3, dice); } private static int straight(int left, int right, Dice dice) { for (int i = left; i <= right; i++) { if (dice.CountOfNum(Dice.DieValues[i]) != 1) return 0; } return dice.Sum(); } public static int SmallStraight(Dice dice) { return straight(left: 0, right: Dice.DieValues.Length - 2, dice:dice); } public static int LargeStraight(Dice dice) { return straight(left: 1, right: Dice.DieValues.Length - 1, dice: dice); } public static int FullHouse(Dice dice) { foreach(DieValue threeKind in Dice.DieValues.Reverse()) { if(dice.CountOfNum(threeKind) == 3) { foreach(DieValue twoKind in Dice.DieValues.Reverse()) { if(dice.CountOfNum(twoKind) == 2) { return ((int)threeKind * 3) + ((int)twoKind * 2); } } } } return 0; } } }

Thursday, October 27, 2016

Open form in the UI thread from another thread

Problem
I have a thread that wakes up and processes stuff based on a schedule and it needs to show progress in a window. This is so that if any user happens to be using the system when this pops up, they can cancel it / see what's going on.

Solution - create a dummy form to Invoke()
        public static Form globalForm;
        private void btnStart_Click(object sender, EventArgs e)
        {
            globalForm = new Form();
            globalForm.Show();
            globalForm.Hide();

            Task t = new Task(Run);
            t.Start();

        }

        Test test; //this is the form i want to show when this job is running
        public void Run()
        {
            test = new Test();
         
            while (true)
            {
                    ShowTestForm();

                //do stuff

                //update status on the window
            }
        }

        private void ShowTestForm()
        {
             /* Note: I prefer this wrapped version over calling globalForm.Invoke(...) from the Run() method. I believe this is cleaner code.  */

            MethodInvoker m = () =>
            {
                if (!test.Visible)
                {
                   test.Show();
                    test.Activate();
                 };
            };
            globalForm.Invoke(m);
        }

Tuesday, September 27, 2016

Error: Subreport could not be shown

Problem
I have a report that has a table with a subreport for each record. When I try to run the report it spins for awhile and then says, "Error: Subreport could not be shown."

Solution
1. Open the parent report's .rdl so that you can see the report XML, not the design surface
2. Search for <Subreport
3. In <ReportName> put .rdl at the end of the subreport's name
4. Upload the parent report to SSRS


Troubleshooting notes
What threw me off here was the fact that it was working inconsistently. I would use the Browse... button in the designer and point to this report. It would pull back the report name without .rdl at the end. This somehow worked. Manually typing in the name in the designer and adding .rdl to the name did not work. I had to open the report in a text editor and edited the name in there.

Friday, September 2, 2016

Posting form data via AJAX to Flask route


SERVER-SIDE - PARSE FORM DATA

@app.route('/something', methods=['POST'])
def post_something():
    something=  request.form['something'];
    return json.dumps({'status':'OK','something':something});
SERVER-SIDE - UNIT TEST
def unittest_post_something(self):
    form = dict(something="hey, i'm unit testing you");
    response = self.app.post("/something", data = form)
    self.assertEqual(response.status_code, 200)


CLIENT-SIDE - POST VIA AJAX
var something = "i'm posting via ajax";

$.ajax({
 url: '/something',
 data: $('form').serialize(),
 type: 'POST',
 success: function(response) {
  console.log(response);
 },
 error: function(error) {
  console.log(error);
 }
});


Ref: http://codehandbook.org/python-flask-jquery-ajax-post/ 

Thursday, August 25, 2016

Network share is not showing new directories / files

Problem
I have a network share that's not updating. I added a new directory and put some files. When I login to another server I cannot see that new directory.

Solution
Add this registry entry:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters] “DirectoryCacheLifetime”=dword:00000000


Ref: https://thecitrixman.com/2013/02/11/scanned-files-not-showing-up-in-network-drives/

Tuesday, August 23, 2016

DateTime.ParseExact is throwing Format Exception when single H specified

Problem
DateTime.ParseExact("9", "H", CultureInfo.InvariantCulture) throws a FormatException.

It also throws the exception for the following cases:
1. "925", "Hmm"
2. "92525", "Hmmss"

Why? Because with a single "H", it tries to grab 1-2 characters. In the case of "925" it tries to grab "92" for the hour.


Solution

Let's say we want to allow the user to enter time in the following formats:

  • H
  • HH
  • Hmm
  • HHmm
  • Hmmss
  • HHmmss

What we can do is pad the string with a 0 to the left if the string length is odd. That is, if the time is in the formats H, Hmm, Hmmss, then we stick a 0 at the beginning and can simply use HH, HHmm, HHmmss.

For example


            string input = "925";
            string format = "";
            if (input.Length % 2 != 0)
                input = input.PadLeft(input.Length + 1, '0');

            switch(input.Length)
            {
                case 2:
                    format = "HH";
                    break;
                case 4:
                    format = "HHmm";
                    break;
                case 6:
                    format = "HHmmss";
                    break;
                default:
                    throw new ArgumentException("Time isn't within the expected lengths of 1-6");
            }

            DateTime dt = DateTime.ParseExact(input, format, CultureInfo.InvariantCulture);


            //do stuff with dt
There was an error in this gadget