Repository: PolicyEngine/policyengine (language: python)

Issue:
Speed up HTTP redirect
#582 was fixed, but it takes about a second. We could speed it up by applying the redirect logic earlier in the JS scripts.
Speed up HTTP redirect
#582 was fixed, but it takes about a second. We could speed it up by applying the redirect logic earlier in the JS scripts.


Discussion / hints:

Fix patch:
diff --git a/changelog.yaml b/changelog.yaml
index fa29fd7c..af257899 100644
--- a/changelog.yaml
+++ b/changelog.yaml
@@ -450,3 +450,11 @@
     fixed:
     - Ensures the docker instance upgrades pip before installing packages.
   date: 2022-03-24 21:40:50
+- bump: minor
+  changes:
+    fixed:
+    - HTTPS redirect sped up using HTML meta tags.
+    changed:
+    - Baseline policy is now editable.
+    added:
+    - Snapshot and reset buttons to the US app.
diff --git a/policyengine-client/public/index.html b/policyengine-client/public/index.html
index 4a21ed9f..8ccdda20 100644
--- a/policyengine-client/public/index.html
+++ b/policyengine-client/public/index.html
@@ -5,6 +5,7 @@
     <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
     <meta name="viewport" content="width=device-width, initial-scale=1" />
     <meta name="theme-color" content="#FFF" />
+    <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
     <meta
       name="description"
       content="See how policy changes affect you and the UK population"
diff --git a/policyengine-client/src/App.jsx b/policyengine-client/src/App.jsx
index 2cc77a92..4878ae63 100644
--- a/policyengine-client/src/App.jsx
+++ b/policyengine-client/src/App.jsx
@@ -22,6 +22,10 @@ import CONTACT from "./markdown/contact.md";
 
 
 export default function App(props) {
+    // Redirect http to https
+    if (!(window.location.hostname.includes("localhost")) && (window.location.protocol !== "https:")) {
+        window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
+    }
     const uk = new UK();
     const us = new US();
     const markdownPages = [
@@ -30,10 +34,6 @@ export default function App(props) {
         { content: CONTACT, path: "/contact", title: "Contact" },
         // Add other pages here
     ];
-    // Redirect http to https
-    if (!(window.location.hostname.includes("localhost")) && (window.location.protocol !== "https:")) {
-        window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
-    }
     return (
         <Router>
             <Switch>
diff --git a/policyengine-client/src/countries/country.jsx b/policyengine-client/src/countries/country.jsx
index 5616dc14..65907583 100644
--- a/policyengine-client/src/countries/country.jsx
+++ b/policyengine-client/src/countries/country.jsx
@@ -7,11 +7,13 @@ export default class Country {
     stateHolder = null
     populationImpactResults = null
     populationImpactBreakdownResults = null
+    editingReform = true;
 
     updatePolicy(name, value) {
         // Update a parameter - validate, then update the state
         let oldPolicy = this.policy;
-        oldPolicy[name].value = value;
+        const targetKey = this.editingReform ? "value" : "baselineValue";
+        oldPolicy[name][targetKey] = value;
         let { policy, policyValid } = this.validatePolicy(oldPolicy);
         this.stateHolder.setCountryState({
             policy: policy,
@@ -37,6 +39,9 @@ export default class Country {
             if (this.policy[key].value !== this.policy[key].defaultValue) {
                 submission[key] = this.policy[key].value;
             }
+            if (this.policy[key].baselineValue !== this.policy[key].defaultValue) {
+                submission["baseline_" + key] = this.policy[key].baselineValue;
+            }
         }
         return submission;
     }
@@ -104,6 +109,8 @@ export default class Country {
 
     useLocalServer = false;
     usePolicyEngineOrgServer = false;
+
+    waitingOnPopulationImpact = false;
 }
 
 export const CountryContext = createContext({name: "uk"});
\ No newline at end of file
diff --git a/policyengine-client/src/countries/uk/components/timeTravel.jsx b/policyengine-client/src/countries/uk/components/timeTravel.jsx
index 96b8a17d..5a43b939 100644
--- a/policyengine-client/src/countries/uk/components/timeTravel.jsx
+++ b/policyengine-client/src/countries/uk/components/timeTravel.jsx
@@ -6,53 +6,22 @@ import { useContext } from 'react';
 import { CountryContext } from '../../country';
 
 export default function TimeTravel(props) {
-    let [reformDate, setReformDate] = React.useState(moment());
+    let [date, setDate] = React.useState(moment());
     const country = useContext(CountryContext);
     let [isLoading, setIsLoading] = React.useState(false);
     return (
         <>
-            <h6 style={{marginTop: 20}}>Baseline snapshot</h6>
-            <p>PolicyEngine will use tax-benefit policy as of the date set below for the baseline simulation</p>
-            <DatePicker 
-                allowClear={false}
-                defaultValue={moment(country.policy.policy_date.value, "YYYYMMDD")} 
-                disabledDate={date => date < moment("2021-01-01") | date > moment("2021-12-31")}
-                onChange={(_, dateString) => {
-                    const url = `${country.apiURL}/parameters?policy_date=${dateString}`;
-                    fetch(url)
-                    .then((res) => {
-                        if (res.ok) {
-                            return res.json();
-                        } else {
-                            throw res;
-                        }
-                    }).then((policy) => {
-                        let previous_policy = country.policy;
-                        const dateInt = +(dateString.replace("-", "").replace("-", ""));
-                        for(let key in previous_policy) {
-                            previous_policy[key].defaultValue = policy[key].value;
-                        }
-                        previous_policy.policy_date.value = dateInt;
-                        previous_policy.policy_date.defaultValue = +(moment().format("YYYYMMDD"));
-                        country.updateEntirePolicy(previous_policy);
-                    }).catch(e => {
-                        message.error("Couldn't time travel - something went wrong." + e.toString());
-                        setIsLoading(false);
-                    });
-                }}
-            />
-            <div style={{paddingBottom: 20}} />
-            <h6 style={{marginTop: 20}}>Reform snapshot</h6>
-            <p>Select a date below to set all parameters to their legislative value as of that date</p>
+            <h6 style={{marginTop: 20}}>Snapshot</h6>
+            <p>Select a date below to set all parameters to their legislative value as of that date.</p>
             <DatePicker
                 allowClear={false}
-                defaultValue={reformDate}
-                onChange={date => setReformDate(date)}
-                disabledDate={date => date < moment("2021-01-01") | date > moment("2021-12-31")}
+                defaultValue={date}
+                onChange={date => setDate(date)}
+                disabledDate={date => date < moment("2021-01-01") | date > moment("2027-12-31")}
             />
             <Button 
                 onClick={() => {
-                    const dateString = reformDate.format("YYYY-MM-DD");
+                    const dateString = date.format("YYYY-MM-DD");
                     const url = `${country.apiURL}/parameters?policy_date=${dateString}`;
                     setIsLoading(true);
                     fetch(url)
@@ -65,14 +34,12 @@ export default function TimeTravel(props) {
                         }).then((policy) => {
                             let previous_policy = country.policy;
                             for(let key in policy) {
-                                if(key !== "baseline_policy_date") {
-                                    previous_policy[key].value = policy[key].value;
-                                }
+                                previous_policy[key][country.editingReform ? "value" : "baselineValue"] = policy[key].value;
                             }
                             country.updateEntirePolicy(previous_policy);
                             setIsLoading(false);
                         }).catch(e => {
-                            message.error("Couldn't time travel - something went wrong." + e.toString());
+                            message.error("Couldn't time travel - something went wrong.");
                             setIsLoading(false);
                         });
                 }}
@@ -80,6 +47,17 @@ export default function TimeTravel(props) {
             >Set policy</Button>
             {isLoading && <Spinner />}
             <div style={{paddingBottom: 20}} />
+            <h6 style={{marginTop: 20}}>Reset</h6>
+            <p>Reset all reform parameters to their values in the baseline.</p>
+            <Button onClick={
+                () => {
+                    let previous_policy = country.policy;
+                    for(let key in country.policy) {
+                        previous_policy[key].value = country.policy[key].baselineValue;
+                    }
+                    country.updateEntirePolicy(previous_policy);
+                }
+            }>Reset reform to baseline</Button>
         </>
   );
 }
\ No newline at end of file
diff --git a/policyengine-client/src/countries/uk/uk.jsx b/policyengine-client/src/countries/uk/uk.jsx
index 0c911b97..c86dab8e 100644
--- a/policyengine-client/src/countries/uk/uk.jsx
+++ b/policyengine-client/src/countries/uk/uk.jsx
@@ -25,6 +25,9 @@ function validatePolicy(policy, defaultPolicy) {
     if (defaultPolicy) {
         for (let parameter in policy) {
             policy[parameter].defaultValue = defaultPolicy[parameter].value;
+            if(policy[parameter].baselineValue === undefined) {
+                policy[parameter].baselineValue = defaultPolicy[parameter].value;
+            }
         }
     }
     if (policy.higher_threshold.value === policy.add_threshold.value) {
diff --git a/policyengine-client/src/countries/us/us.jsx b/policyengine-client/src/countries/us/us.jsx
index 25203c23..00637f8f 100644
--- a/policyengine-client/src/countries/us/us.jsx
+++ b/policyengine-client/src/countries/us/us.jsx
@@ -3,6 +3,7 @@
 */
 
 import Country from "../country"
+import TimeTravel from "../uk/components/timeTravel";
 
 const childNamer = {
     1: "Your first child",
@@ -16,6 +17,9 @@ function validatePolicy(policy, defaultPolicy) {
     if (defaultPolicy) {
         for (let parameter in policy) {
             policy[parameter].defaultValue = defaultPolicy[parameter].value;
+            if(policy[parameter].baselineValue === undefined) {
+                policy[parameter].baselineValue = defaultPolicy[parameter].value;
+            }
         }
     }
     return { policy: policy, policyValid: true };
@@ -45,8 +49,15 @@ export class US extends Country {
     // Vanity URLs
     namedPolicies = {}
     validatePolicy = validatePolicy;
+
+    parameterComponentOverrides = {
+        timeTravel: <TimeTravel />,
+    }
     // Policy page metadata
     parameterHierarchy = {
+        "Snapshot": [
+            "timeTravel",
+        ],
         "IRS": {
             "Income tax schedule": [
                 "irs_income_bracket_rates",
diff --git a/policyengine-client/src/landing.jsx b/policyengine-client/src/landing.jsx
index 8d441462..8e560a21 100644
--- a/policyengine-client/src/landing.jsx
+++ b/policyengine-client/src/landing.jsx
@@ -62,22 +62,10 @@ function LandingPageContent() {
     const in_uk = window.navigator.language === "en-GB";
     const in_us = window.navigator.language === "en-US";
     const outside_uk_us = !in_uk && !in_us;
-    const ukIcon = <Col md={(
-            outside_uk_us ?
-                6 :
-                in_uk ?
-                    7 :
-                    5
-        )} style={{marginBottom: 15}}>
+    const ukIcon = <Col md={6} style={{marginBottom: 15}}>
         <img onClick={() => window.location.href = "/uk/policy"} className="img-fluid" style={{borderRadius: 35, cursor: "pointer"}} alt="UK" src={UKFadedBlue} />
         </Col>;
-    const usIcon = <Col md={(
-            outside_uk_us ?
-                6 :
-                in_us ?
-                    7 :
-                    5
-        )} style={{marginBottom: 15}}>
+    const usIcon = <Col md={6} style={{marginBottom: 15}}>
             <img onClick={() => window.location.href = "/us/household"} className="img-fluid" style={{borderRadius: 35, cursor: "pointer"}} alt="US" src={USFadedBlue} />
         </Col>;
     return <>
diff --git a/policyengine-client/src/policyengine/general/radioButton.jsx b/policyengine-client/src/policyengine/general/radioButton.jsx
new file mode 100644
index 00000000..a4a628d9
--- /dev/null
+++ b/policyengine-client/src/policyengine/general/radioButton.jsx
@@ -0,0 +1,12 @@
+import { Radio } from "antd";
+
+
+export default function RadioButton(props) {
+    return (
+        <div className="justify-content-center d-flex" style={props.style}>
+            <Radio.Group value={props.selected || props.options[0]} buttonStyle="solid" onChange={e => props.onChange(e.target.value)} >
+                {props.options.map(option => <Radio.Button key={option} value={option}>{option}</Radio.Button>)}
+            </Radio.Group>
+        </div>
+    );
+}
\ No newline at end of file
diff --git a/policyengine-client/src/policyengine/pages/household/accountingTable.jsx b/policyengine-client/src/policyengine/pages/household/accountingTable.jsx
index 19fd60d2..782849ef 100644
--- a/policyengine-client/src/policyengine/pages/household/accountingTable.jsx
+++ b/policyengine-client/src/policyengine/pages/household/accountingTable.jsx
@@ -29,7 +29,6 @@ export default class AccountingTable extends React.Component {
     updateBaselineSituation() {
         this.setState({ waitingOnBaseline: true }, () => {
             const submission = this.context.getPolicyJSONPayload();
-            submission.ignoreReform = true;
             let url = new URL(this.context.apiURL + "/calculate");
             url.search = new URLSearchParams(submission).toString();
             fetch(url, {
diff --git a/policyengine-client/src/policyengine/pages/household/household.jsx b/policyengine-client/src/policyengine/pages/household/household.jsx
index d4a11e3f..520e747b 100644
--- a/policyengine-client/src/policyengine/pages/household/household.jsx
+++ b/policyengine-client/src/policyengine/pages/household/household.jsx
@@ -64,7 +64,7 @@ export class Household extends React.Component {
                         <Affix offsetTop={55}>
                             <PolicyOverview />
 				        </Affix>
-                        <Affix offsetTop={400}>
+                        <Affix offsetTop={450}>
                             <SharePolicyLinks page="household"/>
                             <div className="d-block align-middle">
                                 <div className="d-flex justify-content-center">
diff --git a/policyengine-client/src/policyengine/pages/policy/overview.jsx b/policyengine-client/src/policyengine/pages/policy/overview.jsx
index 8080fffe..cb4e7912 100644
--- a/policyengine-client/src/policyengine/pages/policy/overview.jsx
+++ b/policyengine-client/src/policyengine/pages/policy/overview.jsx
@@ -5,16 +5,19 @@ import React, { useContext, useState } from "react";
 import { policyToURL } from "../../tools/url";
 import { getTranslators } from "../../tools/translation";
 import { CountryContext } from "../../../countries";
+import RadioButton from "../../general/radioButton";
 
 const { Step } = Steps;
 
-function generateStepFromParameter(parameter) {
-	if(parameter.value !== parameter.defaultValue) {
+function generateStepFromParameter(parameter, editingReform) {
+	const comparisonKey = editingReform ? "baselineValue" : "defaultValue";
+	const targetKey = editingReform ? "value" : "baselineValue";
+	if(parameter[targetKey] !== parameter[comparisonKey]) {
 		const formatter = getTranslators(parameter).formatter;
-		const changeLabel = (!isNaN(parameter.value) && (typeof parameter.value !== "boolean")) ? 
-			(parameter.value > parameter.defaultValue ? "Increase" : "Decrease") : 
+		const changeLabel = (!isNaN(parameter[targetKey]) && (typeof parameter[targetKey] !== "boolean")) ? 
+			(parameter[targetKey] > parameter[comparisonKey] ? "Increase" : "Decrease") : 
 			"Change";
-		const description = `${changeLabel} from ${formatter(parameter.defaultValue)} to ${formatter(parameter.value)}`
+		const description = `${changeLabel} from ${formatter(parameter[comparisonKey])} to ${formatter(parameter[targetKey])}`
 		return <Step
 			key={parameter.name}
 			status="finish"
@@ -41,12 +44,13 @@ export function OverviewHolder(props) {
 
 export function PolicyOverview() {
     const country = useContext(CountryContext);
-	const plan = Object.values(country.policy).map(generateStepFromParameter).filter(step => step != null);
+	const plan = Object.values(country.policy).map(step => generateStepFromParameter(step, country.editingReform)).filter(step => step != null);
 	const isEmpty = plan.length === 0;
-	const [page, setPage] = useState(1);
 	const pageSize = 4;
+	let [page, setPage] = useState(1);
 	return (
 		<>
+		<RadioButton style={{marginTop: 15}} options={["Baseline", "Reform"]} selected={country.editingReform ? "Reform" : "Baseline"} onChange={option => {country.setState({editingReform: option === "Reform"})}} />
 			<div style={{paddingTop: 20}}></div>
 				{!isEmpty ?
 					<>
@@ -63,9 +67,12 @@ export function PolicyOverview() {
 								/>
 							}
 					</> :
-					<Empty description="You haven't created a reform yet." />
-				}
-		</>
+					<Empty description={
+						country.editingReform ?
+							"You haven't created a reform yet." :
+							"Your reform will be compared against current policy."
+					 } />
+			}		</>
 	);
 }
 
diff --git a/policyengine-client/src/policyengine/pages/policy/parameter.jsx b/policyengine-client/src/policyengine/pages/policy/parameter.jsx
index cd315aef..1c999d1b 100644
--- a/policyengine-client/src/policyengine/pages/policy/parameter.jsx
+++ b/policyengine-client/src/policyengine/pages/policy/parameter.jsx
@@ -22,34 +22,39 @@ function Error(props) {
 }
 
 function BooleanParameterControl(props) {
+	const country = useContext(CountryContext);
 	return <Switch
 		onChange={value => props.onChange(Boolean(value))}
-		checked={props.metadata.value}
+		checked={country.editingReform ? props.metadata.value : props.metadata.baselineValue}
 		className={props.metadata.unit === "abolition" ? "switch-red" : null}
 	/>
 }
 
 function CategoricalParameterControl(props) {
+	const country = useContext(CountryContext);
+	const comparisonKey = country.editingReform ? "baselineValue" : "defaultValue";
+	const targetKey = country.editingReform ? "value" : "baselineValue";
 	return <Select 
 		style={{minWidth: 200}} 
 		showSearch 
-		placeholder={props.metadata.defaultValue} 
+		placeholder={props.metadata[comparisonKey]} 
 		onSelect={props.onChange}>
 		{props.metadata.possibleValues.map(value => (
 			<Option 
 				key={value.key} 
 				value={value.key}
 			>
-				{value.value}
+				{value[targetKey]}
 			</Option>
 		))}
 	</Select>
 }
 
 function StringParameterControl(props) {
+	const country = useContext(CountryContext);
 	return <Input
 		onPressEnter={(e) => {props.onChange(e.target.value)}}
-		defaultValue={props.metadata.value}
+		defaultValue={country.editingReform ? props.metadata.value : props.metadata.baselineValue}
 	/>
 }
 
@@ -65,6 +70,8 @@ function DateParameterControl(props) {
 }
 
 function NumericParameterControl(props) {
+	const country = useContext(CountryContext);
+	const targetKey = country.editingReform ? "value" : "baselineValue";
 	let [focused, setFocused] = useState(false);
 	let { formatter, min, max } = getTranslators(props.metadata);
 	let marks = {[max]: formatter(max)};
@@ -72,12 +79,12 @@ function NumericParameterControl(props) {
 		marks[min] = formatter(min);
 	}
 	const multiplier = props.metadata.unit === "/1" ? 100 : 1;
-	let formattedValue = formatter(props.metadata.value);
-	formattedValue = props.metadata.value === null ? <Spinner /> : formattedValue;
+	let formattedValue = formatter(props.metadata[targetKey]);
+	formattedValue = props.metadata[targetKey] === null ? <Spinner /> : formattedValue;
 	return (
 		<>
 			<Slider
-				value={props.metadata.value}
+				value={props.metadata[targetKey]}
 				style={{marginLeft: min ? 30 : 0, marginRight: 30}}
 				min={min}
 				max={max}
@@ -92,7 +99,7 @@ function NumericParameterControl(props) {
 					<Input.Search 
 						enterButton="Enter" 
 						style={{maxWidth: 300}} 
-						placeholder={multiplier * props.metadata.value} 
+						placeholder={multiplier * props.metadata[targetKey]} 
 						onSearch={value => {
 							setFocused(false); 
 							props.onChange(value / multiplier);
diff --git a/policyengine-client/src/policyengine/pages/policy/policy.jsx b/policyengine-client/src/policyengine/pages/policy/policy.jsx
index c9f35fb0..ae7dfc0c 100644
--- a/policyengine-client/src/policyengine/pages/policy/policy.jsx
+++ b/policyengine-client/src/policyengine/pages/policy/policy.jsx
@@ -52,7 +52,7 @@ export default class Policy extends React.Component {
                         <Affix offsetTop={55}>
                             <PolicyOverview />
 				        </Affix>
-                        <Affix offsetTop={400}>
+                        <Affix offsetTop={450}>
                             <SharePolicyLinks page="policy"/>
                             <div className="d-block align-middle">
                                 <div className="d-flex justify-content-center">
diff --git a/policyengine-client/src/policyengine/pages/populationImpact/populationImpact.jsx b/policyengine-client/src/policyengine/pages/populationImpact/populationImpact.jsx
index 181ccb63..3c20fdd5 100644
--- a/policyengine-client/src/policyengine/pages/populationImpact/populationImpact.jsx
+++ b/policyengine-client/src/policyengine/pages/populationImpact/populationImpact.jsx
@@ -107,12 +107,12 @@ export default class PopulationImpact extends React.Component {
 	static contextType = CountryContext;
 	constructor(props) {
 		super(props);
-		this.state = { waiting: false, error: false };
+		this.state = { error: false };
 		this.simulate = this.simulate.bind(this);
 	}
 
 	componentDidMount() {
-		if (this.context.populationImpactIsOutdated) {
+		if (this.context.populationImpactIsOutdated && !this.context.waitingOnPopulationImpact) {
 			this.simulate();
 		};
 	}
@@ -121,7 +121,7 @@ export default class PopulationImpact extends React.Component {
 		const submission = this.context.getPolicyJSONPayload();
 		let url = new URL(`${this.context.apiURL}/population-reform`);
 		url.search = new URLSearchParams(submission).toString();
-		this.setState({ waiting: true }, () => {
+		this.context.setState({ waitingOnPopulationImpact: true }, () => {
 			fetch(url)
 				.then((res) => {
 					if (res.ok) {
@@ -131,10 +131,12 @@ export default class PopulationImpact extends React.Component {
 					}
 				}).then((data) => {
 					this.context.setState({ populationImpactResults: data, populationImpactIsOutdated: false }, () => {
-						this.setState({ waiting: false, error: false });
+						this.setState({ error: false });
+						this.context.setState({waitingOnPopulationImpact: false});
 					});
 				}).catch(e => {
-					this.setState({ waiting: false, error: true });
+					this.setState({ error: true });
+					this.context.setState({waitingOnPopulationImpact: false});
 				});
 		});
 	}
@@ -146,9 +148,9 @@ export default class PopulationImpact extends React.Component {
 				<Col xl={1} />
 				<Col xl={8}>
 					{
-						(this.state.waiting || (!this.state.error & (this.context.populationImpactResults === null))) ?
-							<Loading message={`Simulating your results on the ${this.context.properName} population (this usually takes about 10 seconds)`} /> :
-							this.state.error ?
+						(this.context.waitingOnPopulationImpact || (!this.state.error & (this.context.populationImpactResults === null))) ?
+						<Loading message={`Simulating your results on the ${this.context.properName} population (this usually takes about 10 seconds)`} /> :
+						this.state.error ?
 								<Loading noSpin message="Something went wrong (try navigating back and returning to this page)" /> :
 								<PopulationResultsPane />
 					}
@@ -158,7 +160,7 @@ export default class PopulationImpact extends React.Component {
 						<Affix offsetTop={55}>
 							<PolicyOverview />
 				        </Affix>
-						<Affix offsetTop={400}>
+						<Affix offsetTop={450}>
 							<SharePolicyLinks page="population-impact"/>
 							<div className="d-block align-middle">
 								<div className="d-flex justify-content-center">
diff --git a/policyengine-client/src/policyengine/policyengine.jsx b/policyengine-client/src/policyengine/policyengine.jsx
index acc6f61c..a30eb7c9 100644
--- a/policyengine-client/src/policyengine/policyengine.jsx
+++ b/policyengine-client/src/policyengine/policyengine.jsx
@@ -66,17 +66,7 @@ export default class PolicyEngine extends React.Component {
             }
         }
         const fetchEndpoint = name => {
-            let url;
-            if (name === "parameters") {
-                const { searchParams } = new URL(document.location);
-                let date = searchParams.get("policy_date");
-                if (date) {
-                    date = `${date.slice(0, 4)}-${date.slice(4, 6)}-${date.slice(6, 8)}`;
-                }
-                url = this.state.country.apiURL + "/" + name + (date ? "?policy_date=" + date : "")
-            } else {
-                url = this.state.country.apiURL + "/" + name;
-            }
+            let url = this.state.country.apiURL + "/" + name;
             fetch(url)
                 .then(response => response.json())
                 .then(data => {
diff --git a/policyengine-client/src/policyengine/tools/url.jsx b/policyengine-client/src/policyengine/tools/url.jsx
index eec0cbc9..4c905cae 100644
--- a/policyengine-client/src/policyengine/tools/url.jsx
+++ b/policyengine-client/src/policyengine/tools/url.jsx
@@ -4,21 +4,25 @@
 
 export function policyToURL(targetPage, policy) {
 	let searchParams = new URLSearchParams(window.location.search);
-	for (const key in policy) {
-		if (policy[key].value !== policy[key].defaultValue) {
-			if(policy[key].unit === "/1") {
-				searchParams.set(key, parseFloat((policy[key].value * 100).toFixed(2)).toString().replace(".", "_"));
-			} else {
+	for (const editingReform of [true, false]) {
+		const targetKey = editingReform ? "value" : "baselineValue";
+		for (const key in policy) {
+			if (policy[key][targetKey] !== policy[key].defaultValue) {
 				let value;
-				try {
-					value = +parseFloat(policy[key].value.toFixed(2));
-				} catch {
-					value = +policy[key].value;
+				if(policy[key].unit === "/1") {
+					value = parseFloat((policy[key][targetKey] * 100).toFixed(2)).toString().replace(".", "_");
+				} else {
+					try {
+						value = +parseFloat(policy[key][targetKey].toFixed(2));
+					} catch {
+						value = +policy[key][targetKey];
+					}
+					value = value.toString().replace(".", "_");
 				}
-				searchParams.set(key, value.toString().replace(".", "_"));
+				searchParams.set(editingReform ? key : `baseline_${key}`, value);
+			} else {
+				searchParams.delete(editingReform ? key : `baseline_${key}`);
 			}
-		} else {
-			searchParams.delete(key);
 		}
 	}
 	const url = `${targetPage}?${searchParams.toString()}`;
@@ -37,9 +41,11 @@ export function urlToPolicy(defaultPolicy, policyRenames) {
 		}
 	}
 	for (const key of searchParams.keys()) {
+		const target = key.includes("baseline_") ? "baselineValue" : "value";
+		const parameterName = key.replace("baseline_", "");
 		try {
-			plan[key].value = +searchParams.get(key).replace("_", ".") / (defaultPolicy[key].unit === "/1" ? 100 : 1);
-		} catch {
+			plan[parameterName][target] = +searchParams.get(key).replace("_", ".") / (defaultPolicy[parameterName].unit === "/1" ? 100 : 1);
+		} catch(e) {
 			// Bad parameter, do nothing
 		}
 	}
diff --git a/policyengine/countries/country.py b/policyengine/countries/country.py
index 61a6d31c..cc1aa190 100644
--- a/policyengine/countries/country.py
+++ b/policyengine/countries/country.py
@@ -88,11 +88,18 @@ def __init__(self):
                 if self.parameter_file is not None
                 else (),
                 self.default_reform,
+                self.Microsimulation.post_reform,
                 use_current_parameters(),
             )
 
             self.baseline = self.Microsimulation(
-                self.default_reform, dataset=self.default_dataset
+                self.default_reform,
+                dataset=self.default_dataset,
+                post_reform=(),
+            )
+
+            self.baseline_system = apply_reform(
+                self.default_reform[:-1], self.system()
             )
 
             self.baseline.simulation.trace = True
@@ -136,12 +143,14 @@ def _get_microsimulations(
                 reform_config["baseline"]["reform"],
                 dataset=self.default_dataset,
                 year=self.default_dataset_year,
+                post_reform=(),
             )
         )
         reformed = self.Microsimulation(
             reform_config["reform"]["reform"],
             dataset=self.default_dataset,
             year=self.default_dataset_year,
+            post_reform=(),
         )
         baseline.year = 2022
         reformed.year = 2022
@@ -243,9 +252,9 @@ def ubi(self, params=None):
     @exclude_from_cache
     def parameters(self, params=None):
         if "policy_date" in params:
-            system = apply_reform(self.default_reform[:-1], self.system())
-            system = use_current_parameters(params["policy_date"])(system)
-            return get_PE_parameters(system)
+            return get_PE_parameters(
+                self.baseline_system, date=params.get("policy_date")
+            )
         return self.policyengine_parameters
 
     @exclude_from_cache
diff --git a/policyengine/countries/uk/default_reform/reform.py b/policyengine/countries/uk/default_reform/reform.py
index 445abf64..307e44e3 100644
--- a/policyengine/countries/uk/default_reform/reform.py
+++ b/policyengine/countries/uk/default_reform/reform.py
@@ -14,6 +14,8 @@
 import yaml
 import pandas as pd
 import warnings
+from openfisca_core.model_api import Reform
+
 
 warnings.filterwarnings("ignore")
 baseline_parameters = CountryTaxBenefitSystem().parameters
@@ -26,7 +28,7 @@ def add_extra_band(parameters: ParameterNode) -> ParameterNode:
         data={
             "threshold": {
                 "description": "An extra Income Tax band for the UK",
-                "values": {"2021-01-01": 1e7},
+                "values": {"2010-01-01": 1e7},
                 "metadata": {
                     "label": "Extra band threshold",
                     "unit": "currency-GBP",
@@ -35,7 +37,7 @@ def add_extra_band(parameters: ParameterNode) -> ParameterNode:
                 },
             },
             "rate": {
-                "values": {"2021-01-01": 0.45},
+                "values": {"2010-01-01": 0.45},
                 "description": "Rate of the extra Income Tax band for the UK",
                 "metadata": {
                     "label": "Extra band rate",
@@ -51,7 +53,7 @@ def add_extra_band(parameters: ParameterNode) -> ParameterNode:
         data={
             "threshold": {
                 "description": "An extra Income Tax band for Scotland",
-                "values": {"2021-01-01": 1e7},
+                "values": {"2010-01-01": 1e7},
                 "metadata": {
                     "label": "Extra band threshold",
                     "unit": "currency-GBP",
@@ -60,7 +62,7 @@ def add_extra_band(parameters: ParameterNode) -> ParameterNode:
                 },
             },
             "rate": {
-                "values": {"2021-01-01": 0.46},
+                "values": {"2010-01-01": 0.46},
                 "description": "Rate of the extra Income Tax band for Scotland",
                 "metadata": {
                     "label": "Extra band rate",
diff --git a/policyengine/countries/uk/reform_parameters.yaml b/policyengine/countries/uk/reform_parameters.yaml
index 8ffeb089..d03ad2c6 100644
--- a/policyengine/countries/uk/reform_parameters.yaml
+++ b/policyengine/countries/uk/reform_parameters.yaml
@@ -2,7 +2,7 @@ reforms:
   policy_date:
     description: Simulate the baseline and reformed systems on this date
     values:
-      2021-01-01: 20210101
+      2010-01-01: 20210101
     metadata:
       label: Policy snapshot
       name: policy_date
@@ -11,7 +11,7 @@ reforms:
     rate:
       description: Tax rate on the unimproved value of land
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Land value tax
         name: LVT
@@ -22,7 +22,7 @@ reforms:
     household_rate:
       description: Tax rate on the unimproved value of land owned by households
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Land value tax (households)
         name: household_lvt
@@ -33,7 +33,7 @@ reforms:
     corporate_rate:
       description: Tax rate on the unimproved value of land owned by corporations
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Land value tax (corporations)
         name: corporate_lvt
@@ -45,7 +45,7 @@ reforms:
     rate:
       description: Price per tonne of carbon emissions
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Carbon tax
         name: carbon_tax
@@ -56,7 +56,7 @@ reforms:
     consumer_incidence:
       description: Proportion of corporate carbon taxes which is passed on to consumers in the form of higher prices (as opposed to shareholders in the form of reduced profitability)
       values:
-        2021-01-01: 1.00
+        2010-01-01: 1.00
       metadata:
         label: Carbon tax consumer incidence
         name: carbon_tax_consumer_incidence
@@ -66,7 +66,7 @@ reforms:
     rate:
       description: A yearly flat tax on net financial wealth
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Wealth Tax
         name: wealth_tax
@@ -78,7 +78,7 @@ reforms:
     rate:
       description: A yearly flat tax on property wealth
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Property Tax
         name: property_tax
@@ -90,7 +90,7 @@ reforms:
     adult:
       description: Flat payment given to each person at or above the adult universal basic income age threshold, but under State Pension age
       values: 
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Working-age adult universal basic income
         name: adult_UBI
@@ -102,7 +102,7 @@ reforms:
     child:
       description: Flat payment given to each person below the adult universal basic income age threshold
       values: 
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Child universal basic income
         name: child_UBI
@@ -114,7 +114,7 @@ reforms:
     senior:
       description: Flat payment given to each person at or above State Pension age
       values: 
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Senior universal basic income
         name: senior_UBI
@@ -126,7 +126,7 @@ reforms:
     WA_adult_UBI_age:
       description: Age at which a person receives the working-age adult universal basic income payment
       values: 
-        2021-01-01: 18
+        2010-01-01: 18
       metadata:
         label: Adult universal basic income age threshold
         name: WA_adult_UBI_age
@@ -135,14 +135,14 @@ reforms:
     taxable:
       description: Include universal basic income as countable income for Income Tax
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Make universal basic income taxable
         name: taxable_UBI
     in_means_tests:
       description: Include universal basic income as countable income for means tested benefits
       values: 
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Count universal basic income in means tests
         name: means_test_UBI
@@ -150,7 +150,7 @@ reforms:
     benefit:
       description: A cash payment made to all households receiving Universal Credit or legacy benefits.
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Cash payment to benefit recipients
         name: benefit_based_cash_payment
@@ -162,7 +162,7 @@ reforms:
     tax:
       description: A cash payment made to all non-benefit-receiving households not paying the higher or additional rates of Income Tax.
       values:
-        2021-01-01: 0.00
+        2010-01-01: 0.00
       metadata:
         label: Cash payment to basic-rate taxpayers.
         name: tax_bracket_based_cash_payment
@@ -174,7 +174,7 @@ reforms:
   abolition:
     abolish_income_tax:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Income Tax
         name: abolish_income_tax
@@ -184,7 +184,7 @@ reforms:
           - increases_net_income: true
     abolish_NI:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish National Insurance
         name: abolish_NI
@@ -195,7 +195,7 @@ reforms:
     abolish_UC:
       description: This does not trigger migration back to legacy benefits
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit
         name: abolish_UC
@@ -206,7 +206,7 @@ reforms:
     abolish_CB:
       description: End all Child Benefit payments
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Child Benefit
         name: abolish_CB
@@ -217,7 +217,7 @@ reforms:
     abolish_HB:
       description: This does not trigger migration to Universal Credit
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Housing Benefit
         name: abolish_HB
@@ -228,7 +228,7 @@ reforms:
     abolish_CTC:
       description: This does not trigger migration to Universal Credit
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Child Tax Credit
         name: abolish_CTC
@@ -239,7 +239,7 @@ reforms:
     abolish_WTC:
       description: This does not trigger migration to Universal Credit
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Working Tax Credit
         name: abolish_WTC
@@ -250,7 +250,7 @@ reforms:
     abolish_SP:
       description:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish State Pension
         name: abolish_SP
@@ -260,7 +260,7 @@ reforms:
           - decreases_net_income: true
     abolish_PC:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Pension Credit
         name: abolish_PC
@@ -271,7 +271,7 @@ reforms:
     abolish_IS:
       description: This does not trigger migration to Universal Credit
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Income Support
         name: abolish_IS
@@ -282,7 +282,7 @@ reforms:
     abolish_JSA_income:
       description: This does not trigger migration to Universal Credit
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish income-based Jobseeker's Allowance
         name: abolish_JSA_income
@@ -292,7 +292,7 @@ reforms:
           - decreases_net_income: true
     abolish_UC_standard_allowance:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit standard allowance
         name: abolish_UC_standard
@@ -303,7 +303,7 @@ reforms:
     abolish_UC_child_element:
       description:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit child element
         name: abolish_UC_child
@@ -313,7 +313,7 @@ reforms:
           - decreases_net_income: true
     abolish_UC_disability_element:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit disability element
         name: abolish_UC_disability
@@ -323,7 +323,7 @@ reforms:
           - decreases_net_income: true
     abolish_UC_carer_element:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit carer element
         unit: abolition
@@ -334,7 +334,7 @@ reforms:
     abolish_UC_childcare_element:
       description:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit childcare element
         name: abolish_UC_childcare
@@ -345,7 +345,7 @@ reforms:
     abolish_UC_housing_costs_element:
       description:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Universal Credit housing costs element
         name: abolish_UC_housing_costs
@@ -354,7 +354,7 @@ reforms:
     abolish_ESA_income:
       description:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish income-based Employment and Support Allowance
         variable: ESA_income
@@ -364,7 +364,7 @@ reforms:
           - decreases_net_income: true
     abolish_CT:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Council Tax
         name: abolish_CT
@@ -376,7 +376,7 @@ reforms:
             revenue: -38.9e9
     abolish_sdlt:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Stamp Duty Land Tax
         name: abolish_sdlt
@@ -386,7 +386,7 @@ reforms:
           - corporate_sdlt
     abolish_ltt:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Land Transaction Tax (Wales)
         name: abolish_ltt
@@ -394,7 +394,7 @@ reforms:
         variable: ltt_on_transactions
     abolish_lbtt:
       values:
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Abolish Land and Buildings Transaction Tax (Scotland)
         name: abolish_lbtt
@@ -403,14 +403,14 @@ reforms:
   abolish_marriage_allowance_income_condition:
     description: The Marriage Allowance is currently withheld from higher and additional rate taxpayers. This switch allows them to receive it.
     values:
-      2021-01-01: false
+      2010-01-01: false
     metadata:
       label: Abolish the Marriage Allowance income condition
       name: abolish_marriage_allowance_income_condition
       unit: abolition
   abolish_business_rates:
     values:
-      2021-01-01: false
+      2010-01-01: false
     metadata:
       label: Abolish business rates
       name: abolish_business_rates
@@ -421,7 +421,7 @@ reforms:
       amount:
         description: The single pensioner supplement is paid to people over State Pension age who live alone.
         values:
-          2021-01-01: 0
+          2010-01-01: 0
         metadata:
           name: SPS_amount
           label: Single pensioner supplement amount
@@ -430,7 +430,7 @@ reforms:
       reduction_rate:
         description: The rate at which the single pensioner supplement is reduced with non-benefit income.
         values:
-          2021-01-01: 0
+          2010-01-01: 0
         metadata:
           name: SPS_reduction_rate
           label: Single pensioner supplement reduction rate
@@ -438,7 +438,7 @@ reforms:
       reduction_threshold:
         description: The threshold for market (non-benefit) income to reduce the single pensioner supplement.
         values:
-          2021-01-01: 0
+          2010-01-01: 0
         metadata:
           name: SPS_reduction_threshold
           label: Single pensioner supplement reduction threshold
@@ -447,7 +447,7 @@ reforms:
       takeup_rate:
         description: The percentage of eligible recepients that claim the single pensioner supplement. By default, this is the same take-up rate as Pension Credit, the main means-tested benefit for pensioners.
         values:
-          2021-01-01: 0.63
+          2010-01-01: 0.63
         metadata:
           name: SPS_takeup_rate
           label: Single pensioner supplement take-up rate
@@ -456,7 +456,7 @@ reforms:
     exempt_seniors_from_PA_changes:
       description: Exempt individuals over State Pension Age from reforms to the Personal Allowance.
       values: 
-        2021-01-01: false
+        2010-01-01: false
       metadata:
         label: Exempt seniors from PA reforms
         name: exempt_seniors_from_PA_reforms
diff --git a/policyengine/utils/reforms.py b/policyengine/utils/reforms.py
index a259add5..7b1114ee 100644
--- a/policyengine/utils/reforms.py
+++ b/policyengine/utils/reforms.py
@@ -178,17 +178,22 @@ def flow_breakdown_parameter_metadata_down(
     return parameters
 
 
-def get_PE_parameters(system: TaxBenefitSystem) -> Dict[str, dict]:
+def get_PE_parameters(
+    system: TaxBenefitSystem, date: str = None
+) -> Dict[str, dict]:
     """Extracts PolicyEngine parameters from OpenFisca parameter metadata.
 
     Args:
         system (TaxBenefitSystem): The tax-benefit system to extract from.
+        date (str): The date to extract parameters for. Defaults to the current date.
 
     Returns:
         Dict[str, dict]: The parameter metadata.
     """
 
     now = datetime.now().strftime("%Y-%m-%d")
+    if date is not None:
+        now = datetime.strptime(date, "%Y-%m-%d").strftime("%Y-%m-%d")
     parameters = []
     system.parameters = flow_breakdown_parameter_metadata_down(
         system.parameters, system.variables
@@ -221,7 +226,7 @@ def get_PE_parameters(system: TaxBenefitSystem) -> Dict[str, dict]:
                 name=name,
                 parameter=parameter.name,
                 description=parameter.description,
-                label=parameter.metadata.get("label", name),
+                label=parameter.metadata["label"],
                 value=parameter(now)
                 if isinstance(parameter, Parameter)
                 else None,
@@ -340,7 +345,7 @@ def create_reform(
                 f"{str_value[:4]}-{str_value[4:6]}-{str_value[6:8]}"
             )
         elif param != "household":
-            metadata = policyengine_parameters[param]
+            metadata = policyengine_parameters[param.replace("baseline_", "")]
             name = metadata["label"]
             description = get_summary(metadata, value)
             if metadata["valueType"] == "bool":
diff --git a/setup.py b/setup.py
index 7804bc56..44c222a9 100644
--- a/setup.py
+++ b/setup.py
@@ -8,9 +8,9 @@
     license="http://www.fsf.org/licensing/licenses/agpl-3.0.html",
     url="https://github.com/policyengine/policyengine",
     install_requires=[
-        "OpenFisca-UK==0.14.3",
+        "OpenFisca-UK==0.17.0",
         "OpenFisca-US==0.37.7",
-        "OpenFisca-Tools>=0.7.0",
+        "OpenFisca-Tools>=0.7.2",
         "plotly",
         "flask",
         "itsdangerous==2.0.1",


Test patch:
diff --git a/policyengine/tests/server/populations/test_population_metrics.py b/policyengine/tests/server/populations/test_population_metrics.py
index 6b251465..dac6b2d4 100644
--- a/policyengine/tests/server/populations/test_population_metrics.py
+++ b/policyengine/tests/server/populations/test_population_metrics.py
@@ -20,7 +20,7 @@
     dict(net_cost_numeric=(0, 0)),
     dict(net_cost_numeric=(-110e9, -90e9)),
     dict(net_cost_numeric=(-6e9, -3e9)),
-    dict(net_cost_numeric=(10e9, 30e9)),
+    dict(net_cost_numeric=(10e9, 35e9)),
     dict(loser_share=(0, 1e-3)),
 )